Skip to main content

  | मौसम…

411 समाचार
Public archive edition · Attributed source reporting with original dates · AI drafts are excluded

411 REFERENCE · Wikibooks

A-level Computing 2009/AQA/Problem Solving, Programming, Data Representation and Practical Exercise/Skeleton code/2011 Exam/Section D

Reference material by Wikibooks contributors. This retains its source identity and is not 411 news reporting.

Code Discussion When you first run your code you will probably find that everything appears to work. Don't be deceived, try and load the top scores. It probably won't work; we need to get the top scores file in the correct location, so that when we select the load the option from the menu it actually loads the file: Correct Load Location Inside the LoadTopScores() sub routine you'll find the line that is giving you trouble FileOpen(1, "HiScores.txt", OpenMode.Input) All this line is doing is trying to find the HiScores.txt file inside the folder where your code is running. On Windows machines this will be: ConsoleApplication1\bin\Debug You can fix this by copying the HiScores.txt file into here. Or alternatively you can copy the file into the root of your project, in this case being the poorly named project: ConsoleApplication1 We then need to make the line we looked at above point to this file: FileOpen(1, "../../HiScores.txt", OpenMode.Input) The ../../ means to take the Debug folder where the code is running, and to look two folders up to find the HiScores.txt file: If the code is running in ConsoleApplication1\bin\Debug "../../HiScores.txt" will find the file in ConsoleApplication1\ Potential Questions This list is by no means a list of the questions you will be asked in the exam, but by solving these problems you should get to understand the code a little better. Validating the LoadTopScores(TopScores) You might be worried about people accidentally overwriting the top scores from the main menu. Imagine that you have just played the game for an hour and you accidentally click on option 3 on the main menu, you'd overwrite all your scores! How could we validate the entry of the load game to make sure that this didn't happen: The option menu Add a new option to the menu to: Reset the top scores An option that appears to be missing, is the ability for the user to reset the top scores that are currently stored in the TopScores array. We need to add an option (option 5) to the menu so that users can manually reset the top scores. Make sure that you get all the menu option checks to allow for the new menu option number. Answer:       1. Inside the DisplayMenu() sub routine add the following to allow people to select the number 5 Console.WriteLine("4. Display top scores") Console.WriteLine("5. Reset top scores")'added option 5 Console.WriteLine("9. Quit") 2. Inside the GetMenuChoice() function make sure that option choice 5 does not bring up an error by making the following change: If OptionChosen < 1 Or (OptionChosen > 4 And OptionChosen <> 9) Then Update this to: If OptionChosen < 1 Or (OptionChosen > 5 And OptionChosen <> 9) Then 3. Update the main() sub routine by making sure that option 5 does not bring up an error Loop Until (OptionSelected >= 1 And OptionSelected <= 4) Or OptionSelected = 9 Update this to: Loop Until (OptionSelected >= 1 And OptionSelected <= 5) Or OptionSelected = 9 4. Finally we need to add the code to run when a person selects option 5 If OptionSelected >= 1 And OptionSelected <= 4 Then Select Case OptionSelected Case 1 : PlayDiceGame(PlayerOneName, PlayerTwoName, True, TopScores) Case 2 : PlayDiceGame(PlayerOneName, PlayerTwoName, False, TopScores) Case 3 : LoadTopScores(TopScores) Case 4 : DisplayTopScores(TopScores) End Select End If Make the following changes If OptionSelected >= 1 And OptionSelected <= 5 Then 'change! Select Case OptionSelected Case 1 : PlayDiceGame(PlayerOneName, PlayerTwoName, True, TopScores) Case 2 : PlayDiceGame(PlayerOneName, PlayerTwoName, False, TopScores) Case 3 : LoadTopScores(TopScores) Case 4 : DisplayTopScores(TopScores) Case 5 : ResetTopScores(TopScores) 'addition! End Select End If   Starting the game Making sure each player has a different name When you start the game the following scenario could occur: What is player one's name? Aubrey What is player two's name? Aubrey This means that you wouldn't know who was who, which Aubrey is which? It would be far better if we forced the users to have different names. To do this you are going to need a loop to check that the second name is different from the first name: Answer:       Console.Write("What is player one's name? ") PlayerOneName = GetValidPlayerName() Console.Write("What is player two's name? ") Do PlayerTwoName = GetValidPlayerName() If PlayerOneName = PlayerTwoName Then Console.Write("Name is in use already. Please enter new one ") End If Loop Until PlayerTwoName <> PlayerOneName The above would allow: Ryan and ryan If you don't want the check to be case sensitive, we are going to check that the capitalised versions of both names are not the same by using the UCase("String") function. In this case UCase("String") would equal "STRING" Console.Write("What is player one's name? ") PlayerOneName = GetValidPlayerName() Console.Write("What is player two's name? ") Do PlayerTwoName = GetValidPlayerName() If UCase(PlayerOneName) = UCase(PlayerTwoName) Then Console.Write("Name is in use already. Please enter new one ") End If Loop Until UCase(PlayerTwoName) <> Ucase(PlayerOneName) The above would not allow: Ryan and ryan   Save current top scores to the CSV file A glaring omission from the code is the ability to save the current top scores back to the CSV file. You might well be asked to implement this. Let's first take a look at the code that reads the data from the file: Sub LoadTopScores(ByRef TopScores() As TTopScore) Dim Count As Integer Dim Count2 As Integer Dim LineFromFile As String Dim ValuesOnLine(2) As String FileOpen(1, "../../HiScores.txt", OpenMode.Input) For Count = 1 To MaxSize ValuesOnLine(1) = "" ValuesOnLine(2) = "" LineFromFile = LineInput(1) Count2 = 0 Do ValuesOnLine(1) = ValuesOnLine(1) + LineFromFile(Count2) Count2 = Count2 + 1 Loop Until LineFromFile(Count2) = "," Count2 = Count2 + 1 Do ValuesOnLine(2) = ValuesOnLine(2) + LineFromFile(Count2) Count2 = Count2 + 1 Loop Until Count2 = LineFromFile.Length TopScores(Count).Name = ValuesOnLine(1) TopScores(Count).Score = CInt(ValuesOnLine(2)) Next FileClose(1) End Sub There are three important things that you need to gain from this. 1. this line opens the file in read only mode and allocates it to file variable 1, we need another mode that writes to the file FileOpen(1, "../../HiScores.txt", OpenMode.Input) 2. This code reads a line from the file and saves it into the LineFromFile string. Notice the 1 is referring to the file variable. This line is inside a loop that loops from on the variable Count, why is this? LineFromFile = LineInput(1) Answer:       The code for the loop is: For Count = 1 To MaxSize ... LineFromFile = LineInput(1) ... Next This cycles through 1 to 4, meaning that the code reads all four lines of the text file.   3. Finally we have some house keeping, where we need to close the file to make sure we free up resources FileClose(1) Using these ideas we need to create a sub routine to save the files. Let's get some pseudo code together: Pseudo Code of Save Data to File sub savefiles(TopScores()) open the text file for writing loop through 1 to 4 with count write TopScores(count) to the text file in a CSV format close the file end sub But what real code can we use to do the above? 1. open file for writing, use Output instead of Input FileOpen(1, "../../HiScores.txt", OpenMode.Output) 2. Print a line to a file instead of reading from it. Remember you will need a loop to write all 4 lines PrintLine(1, "...") 'write a line to file 1. Make sure the line you write is in CSV format When you are writing to the file, remember that each line you are writing must have this format: <name><comma><score> Norman,76 And you will get this data from the TopScore() parameter that you are passing to the sub routine 3. Closing the file is the same as before FileClose(1) Try the code out yourself and see if you can get it to work: Answer:       Sub SaveTopScores(ByRef TopScores() As TTopScore) Dim Count As Integer FileOpen(1, "../../HiScores.txt", OpenMode.Output) For Count = 1 To MaxSize PrintLine(1, TopScores(Count).Name & "," & TopScores(Count).Score) Next FileClose(1) End Sub You can also achieve this by using a stream writer: Imports System.IO ' you might need this to use StreamWriter Sub SaveTopScores(ByRef TopScores() As TTopScore) Dim filewriter As StreamWriter Dim filename As String filename = "../../HighScores.txt" filewriter = New StreamWriter(filename) For Count = 1 To MaxSize filewriter.WriteLine(TopScores(Count).Name & "," & TopScores(Count).Score) Next filewriter.Close() End Sub   CSV file add more fields to the CSV file when saving number of balls survived average score per ball Displaying scores You might be asked to display results in different ways, there are several potential questions, be ready for them all. Find the highest score To do this we need to add a new sub routine, let's call it DisplayHighestScore, and we'll need to pass it the TopScore data structure: Pseudo Code of displaying a top score sub displaytopscore(TopScores()) create a pointer to the topscore: hiscore set hiscore = 1 so that it points to the first element in TopScores go through each of the TopScores if topscores value is bigger than value being pointed to by hiscores then set hiscores to point to new top score print out the TopScores(hiscore) end sub Try to code this solution yourself: Answer:       Sub DisplayHighestScore(ByRef TopScores() As TTopScore) Dim HighestRec As Integer 'stores the location of the highest record Dim Count as Integer = 1 HighestRec = 1 'set the top score to the first person For Count = 1 To MaxSize 'if subsequent people have higher scores, place them as top score If TopScores(Count).Score > TopScores(HighestRec).Score Then HighestRec = Count End If Next Console.Writeline("Top Score: " & TopScores(HighestRec).Name & " : " & TopScores(HighestRec).Score) End Sub Now we have written the code to find out the top score we also need to adjust the code so that the program can call it, by using a menu option, the option 5. See the code above on Resetting Top Scores to do this Draw a trace table for the following: Michael, 45 Dave, 78 Gerald, 23 Colin, 75 Answer:       Count HiScore TopScores 1 2 3 4 name score name score name score name score 1 1 Michael 45 Dave 78 Gerald 23 Colin 75 2 2 3 2 4 2 The output should be: Top Score: Dave : 78   What about these people: Michelle, 67 Davina, 54 Geraldine, 13 Coleen, 67 Answer:       Count HiScore TopScores 1 2 3 4 name score name score name score name score 1 1 Michelle 67 Davina 54 Geraldine 13 Coleen 67 2 1 3 1 4 1 The output should be: Top Score: Michelle : 67     Find the lowest score To do this we need to add a new sub routine, let's call it DisplayLowestScore, and we'll need to pass it the TopScore data structure: Pseudo Code of displaying a top score sub displaylowestscore(TopScores()) create a pointer to the topscore: hiscore set hiscore = 1 so that it points to the first element in TopScores go through each of the TopScores if topscores value is smaller than value being pointed to by hiscores then set hiscores to point to new lowest score print out the TopScores(hiscore) end sub Try to code this solution yourself: Answer:       Sub DisplayLowestScore(ByRef TopScores() As TTopScore) Dim LowestRec As Integer 'stores the location of the highest record Dim Count as Integer = 1 LowestRec = 1 'set the top score to the first person For Count = 1 To MaxSize 'if subsequent people have higher scores, place them as top score If TopScores(Count).Score < TopScores(LowestRec).Score Then LowestRec = Count End If Next Console.Writeline("Lowest Score: " & TopScores(LowestRec).Name & " : " & TopScores(LowestRec).Score) End Sub Now we have written the code to find out the top score we also need to adjust the code so that the program can call it, by using a menu option, the option 5. See the code above on Resetting Top Scores to do this Draw a trace table for the following: Michael, 45 Dave, 78 Gerald, 23 Colin, 45 Answer:       The output should be: Lowest Score: Gerald : 23   What about these people: Michelle, 67 Davina, 13 Geraldine, 54 Coleen, 67 Answer:       The output should be: Lowest Score: Davina : 13     Printing out all the scores in order You might be asked to print out all the high scores in order. That means we need to order the items. We can do this in two ways: Bubble Sort They could well ask you to perform a full sort on the high score table. A bubble sort is a potential solution to doing this. Take a look at the following pseudo code to perform a bubble sort. You need to make your own version of this to sort all the data: '''procedure''' bubbleSort( A ''':''' list of sortable items ) '''do''' swapped = false '''for each''' i '''in''' 1 '''to''' length(A) - 1 '''inclusive do:''' '''if''' A[i-1] > A[i] '''then''' swap( A[i-1], A[i] ) swapped = true '''end if''' '''end for''' '''while''' swapped '''end procedure''' Create a sub routine called bubbleSort. It should have the following features: *TopScores passed as a Parameter, should you use byRef or byVal?       If you want to re-order the TopScores array you'll have to talk to the array directly by using ByRef. ByVal will only make a copy of the array so any changes made to re-order it will be lost when the subroutine finishes.   reorder the TopScores array look at the pseudo code above remember you are dealing with an array of records prints out the sorted array using: Console.Writeline(Count & ": " & TopScores(Count).Name & " " & TopScores(Count).Name) called from menu item 5. Answer:       Sub bubbleSort(ByRef TopScores() As TTopScore) dim swapped as boolean dim temp as TTopScore 'sort the elements Do Swapped = False For Count = 1 to MaxSize - 1 If TopScores(Count).score < TopScores(Count + 1).score Then temp.Name = TopScores(Count).Name temp.Score = TopScores(Count).Score TopScores(Count) = TopScores(Count + 1) TopScores(Count + 1).Name = temp.Name TopScores(Count + 1).Score = temp.Score Swapped = True End If Next Loop While Swapped = True 'Print out the elements For Count = 1 to MaxSize Console.Writeline(Count & ": " & TopScores(Count).Name & " " & TopScores(Count).Score) Next End Sub Now we have written the code to order the scores we also need to adjust the code so that the program can call it, by using a menu option, the option 5. See the code above on Resetting Top Scores to do this Draw a trace table for the following: Michael, 45 Dave, 78 Gerald, 23 Colin, 75 Answer:       Swapped Count MaxSize Temp TopScores name score 1 2 3 4 name score name score name score name score False 1 4 null null Michael 45 Dave 78 Gerald 23 Colin 75 True 1 4 Michael 45 Dave 78 Michael 45 True 2 4 True 3 4 Gerald 23 Colin 75 Gerald 23 False 1 4 True 2 4 Michael 45 Colin 75 Michael 45 True 3 4 False 1 4 False 2 4 False 3 4 The output should be: 1: Dave 78 2: Colin 75 3: Michael 45 4: Gerald 23     Hard Coded List the number of balls survived To do this we are going to need to implement a counting variable to keep track of the number of balls survived by each person and print this out when they are out: You are out. Your final score was: 176 You survived 34 balls As this variable is a whole number we are going to use an integer to store it: Answer:       A quick way to solve this is by adding the following as a local variable to the PlayDiceGame procedure. Dim BallsSurvived As Integer = 0 We then need to make sure number BallsSurvived is set to 0 for each new batter. At the beginning of the PlayDiceGame procedure For PlayerNo = 1 To 2 ' give each player a turn CurrentPlayerScore = 0 'reset the score BallsSurvived = 0 'reset the number of balls survived One successfully surviving each ball we want to increase the number of balls survived Console.WriteLine() Console.WriteLine("Press the Enter key to continue") Console.ReadLine() If PlayerOut = False Then BallsSurvived = BallsSurvived + 1 End If Loop Until PlayerOut '=true Finally we want to boast how many balls they survived Loop Until PlayerOut '=true Console.WriteLine("You are out. Your final score was: " & CurrentPlayerScore) Console.WriteLine("You survived " & BallsSurvived &" balls") Console.WriteLine() Extensions: Display the average number of runs per ball: Answer:       We know the number fo runs through: CurrentPlayerScore and we know the number of balls survived through: BallsSurvived Console.WriteLine("You are out. Your final score was: " & CurrentPlayerScore) Console.WriteLine("You survived " & BallsSurvived &" balls") Console.WriteLine("Average runs per ball = " & CurrentPlayerScore / BallsSurvived) Console.WriteLine()     Keeping track of sixes and fours You might be asked to put some code in place to keep track of how many sixes and fours were scored by each side. Once each person has finished batting the score of that person should be displayed alongside the numbers of 4s and 6s: You are out. Your final score was: 176 You got: 2 sixes and 9 fours. You can solve this in several ways so don't be worried if you have a different solution to the one below: Answer:       A quick way to solve this is by adding the following as global variables: Dim numofsixes As Integer Dim numoffours As Integer We then need to make sure they are set to 0 for each new batter. At the beginning of the PlayDiceGame procedure For PlayerNo = 1 To 2 ' give each player a turn CurrentPlayerScore = 0 'reset the score numofsixes = 0 numoffours = 0 We then need to catch each time that they score a four or a six. Remember that the a 3 on the die means four runs are scored, a 4 means six runs. If BowlDieResult >= 1 And BowlDieResult <= 4 Then RunsScored = CalculateRunsScored(BowlDieResult) If BowlDieResult = 4 Then numofsixes = numofsixes + 1 End If If BowlDieResult = 3 Then numoffours = numoffours + 1 End If Finally we need to display the score when they are finally out: Loop Until PlayerOut '=true Console.WriteLine("You are out. Your final score was: " & CurrentPlayerScore) Console.WriteLine("You got:" & numoffours & " fours and " & numofsixes & " sixes.") Alternatively you could add the information on fours and sixes to the record definition: Structure TTopScore Dim Name As String Dim Score As Integer Dim sixes As Integer '## Dim fours As Integer '## End Structure but the coding is a little more complex. Give it a go if you're feeling brave.   Changing the number of scores in stored in TopScores We'll need to adjust some things here Answer:       Adjust the MAXSIZE to equal 5 We'll also need to add an extra set of values to the HiScores.txt file. As it will produce an error when loading them for the first time, as it will try to load a line that doesn't exist.   Adding a new way to be out Add a new way to be out: "Run out". Answer:       To do this we are going to have to add another option to our appeal dice, making it 5 sided. We are then going to have to build a new case for case 5 which will say "Run out"   Drawn games put a routine in to handle what happens when a game is drawn This isn't too hard... If PlayerOneScore = PlayerTwoScore Then Console.WriteLine("You have drawn!") End If Handling how drawn scores update the high score list if there is only one current high score is less than the two drawn scores is much trickier! Monty,1 is the current lowest score.. so if both players score 2 runs and draw the game what scores should be recorded. The program currently will record the high score for player one.... then when the routine checks it returns that player two doesn't have a high score (i.e. a score that is higher than any of the scores held in the topscores array). This is because the playerOne score has already been recorded. There are only four possible solutions: Neither score is recorded Both scores are recorded, adding an extra record (element) to the array Concatenate both player names and assign the playerName field of the Topscores array of records Have only one score recorded BUT... select which of the players Names is added Randomly Using a prompt to allow the user to decide Playing the game Randomise who goes first Manual dice validation If you play the game with real dice and you type in a string of letters instead of a number the program will crash. We need to catch this error and fix it. By now you should be familiar with the Try Catch code Try something that might go wrong Catch ex As Exception do something to fix or flag up the problem End Try What line in the RollBowlDie function is the problem occurring at? There are two other areas that need fixing, where are they? Answer:       Try BowlDieResult = Console.ReadLine 'line that causes the problem Catch ex As Exception BowlDieResult = 0 'set the result to an acceptable error number so they don't score any points for this misbowl End Try This is also a problem with the RollAppealDie function Try AppealDieResult = Console.ReadLine 'line that causes the problem Catch ex As Exception AppealDieResult = 0 'set the result to an acceptable error number so they don't score any points for this appeal End Try This is also a problem with the GetMenuChoice function Try OptionChosen = Console.ReadLine 'line that causes the problem Catch ex As Exception OptionChosen = 0 'set the result to an acceptable error number so you don't accidentally launch any functions End Try   They might ask you to keep looping until with an error message until the user inputs a correct answer, to do this you'll need a loop: Answer:       Dim validation As Boolean = False Do Try BowlDieResult = Console.ReadLine validation = True Catch Console.WriteLine("Invalid answer. Try again.") End Try Loop Until validation = True   Only one entry per person If the player's name is already in the TopScores array then update the value rather than adding someone more than once. The useless: DisplayCurrentPlayerNewScore This subroutine isn't used (isn't called anywhere). They might ask you to call this function from the PlayDiceGame sub, or elsewhere in the code. This code has one parameter: CurrentPlayerScore As Integer Sub DisplayCurrentPlayerNewScore(ByVal CurrentPlayerScore CurrentPlayerScore As Integer) Where might we use it? Where is there similar functionality already? Take a look at the PlayDiceGame code: CurrentPlayerScore = CurrentPlayerScore + RunsScored Console.WriteLine("Your new score is: " & CurrentPlayerScore) 'this line performs the same function End If So we could easily replace this line with the a call to the code above: CurrentPlayerScore = CurrentPlayerScore + RunsScored DisplayCurrentPlayerNewScore(CurrentPlayerScore) 'this line replaces the previous one End If They might also ask you to do add a little to the complexity of the DisplayCurrentPlayerNewScore sub routine. For example, make it so that each score displayed looks like: ############### #Current Score# ############### # 45 # ############### Try and implement this Set limit on the number of balls In the current skeleton program, the players are permitted to carry on playing the game as long as the appeal dice is not greater than or equal to 2. Therefore, we may be asked to set a constraint on the maximum number of balls available for each player. In the following program code, I will demonstrate how the aforementioned problem can be tackled. The limit is typed in each time the game is played, making it possible for the players to set the limit in the way they want. Function BallLimit() As Integer Dim NumberOfBalls As Integer = 0 Console.Write("Do you want the game to have a limit on the number of balls?(Y/N) ") Dim Choice As Char = Console.ReadLine If Choice = "Y" Then Do Console.WriteLine() Console.Write("Enter the maximum number of balls: ") NumberOfBalls = Console.ReadLine Loop Until NumberOfBalls > 0 ElseIf Choice = "N" Then NumberOfBalls = 0 End If BallLimit = NumberOfBalls End Function I create a function call BallLimit The limit is represented by the variable NumberOfBalls Firstly, the player is asked whether he wants to set a limit or not. If the answer is "Y", he will have to enter the limit, which must be greater than 0 (it is not realistic to have no balls). Otherwise, the limit is set to 0 The value of the variable is copied to the integer BallLimit, which is then returned to the subroutine PlayDiceGame as you will see The following code is written in the PlayDiceGame subroutine Dim x As Integer Dim NumberOfBalls As Integer NumberOfBalls = BallLimit() 2 new variables are declared. The variable NumberOfBalls will take the value returned by the function BallLimit as stated above For PlayerNo = 1 To 2 x = 0 CurrentPlayerScore = 0 PlayerOut = False Console.WriteLine() If PlayerNo = 1 Then Console.WriteLine(PlayerOneName & " is batting") Else Console.WriteLine(PlayerTwoName & " is batting") End If Each time a new player starts the game, the value of x is set to 0 Do x = x + 1 BowlDieResult = RollBowlDie(VirtualDiceGame) If BowlDieResult >= 1 And BowlDieResult <= 4 Then Each time the Do loop is executed the value of x is incremented by 1. We must also change the condition of the Do loop as follow: Loop Until x = NumberOfBalls or PlayerOut So how does this actually work? If the player set a limit on the number of balls, say 10, the number is passed to the variable NumberOfBalls in the PlayDiceGame subroutine. Each time the player rolls the bowl dice, one ball is deemed to be lost. That's why the x is incremented by 1 each time. It is used to keep track of the number of balls that have been used so far. Therefore, when the value of x reaches 10, the loop will stop because the player runs out of balls. The player may be out before the limit is reached. In this case, the condition PlayerOut is executed. If the player did not set a limit, the number 0 is passed to the variable NumberOfBalls in the PlayDiceGame subroutine. The value of x when it enters the first Do loop is x = 0 + 1 = 1. It will keep increasing thereafter and so x is always greater than 1. In other words, the condition x = NumberOfBalls will NEVER be satisfied in this case. PlayerOut is the only condition that may work. Therefore, there is no limit on the number of balls Linear Search You need to implement a linear search: Please insert the name you are looking for: Kenny to tell you either: Kenny scored 5. or: We couldn't find a user named Kenny. Try again. Sub LinearSearch(ByRef TopScores() As TTopScore) Dim nameSought As String Dim found As Boolean Dim position As Integer Do Console.WriteLine("Enter the name you'd like to search for: ") nameSought = Console.ReadLine() found = False position = 1 Do If TopScores(position).Name = nameSought Then found = True Else position = position + 1 End If Loop Until position > MaxSize Or found = True If found <> True Then Console.WriteLine("We couldn't find a user named " & nameSought & ". Try again.") Else Console.WriteLine(TopScores(position).Name & " scored " & TopScores(position).Score & ".") End If Loop While found <> True 'loops back to re-entering a name if found = false. End Sub