Part 7: Thinking about thinking
In which we'll take the player's guess, compare it to the real solution, and work out how accurate his guess was.
One of the main things that people have problems with when programming is begin able to take an idea in their head, and turn that idea into computer code that does exactly what they want it to do. What I want to show you in this chapter is how we take one concept and turn it into code.
The key (as always) is being able to break your idea into smaller and smaller rules. If you break it down far enough, then you'll have something that can easily be written in OPL. What you must remember is when writing these rules, you must mention everything you do. You can't say "compare this ball to those four," you need to say what you're comparing, to which ball, and what to do depending on the result.
Checking The Guess
Breaking It Down
Before we can tell the computer what to do, we need to have a clear understanding on the thought processes that that our brain goes through. If we were making a cup of tea, it's no good having a rule that goes:
You need something like:
Take kettle to tap Remove lid Put kettle under tap Turn on tap DO
wait a bit UNTIL kettle is full Put lid on kettle Put kettle on table Plug in power cable to kettle (it's an old fashioned one) Switch on power at mains Switch on kettle DO wait a bit UNTIL kettle switch moves to off position
Of course, there's nothing to stop you calling the above PROC boil_kettle: is there.
Right, so this is the level of detail we'll need to break down any computer action. Let's now look at MASTER and how I've broken it down, although you might want to have a go yourself before having a look at my solution.
Again, remember there is no right or wrong way when doing routines like this. If your way works, great! Just because it's slightly different from mine, you understand it and the user gets the same end result.
Checking Those Balls
The routine in Master checks to see how many balls are the right colour in the right place, and how many are the right colour but in the wrong place. These two numbers will be used to display the 'hint' to the player so he or she can make another guess to try and logically work out the solution.
Always keep your overall goal of a procedure in mind. It helps immensely. Now…
So what we'll do here is break down exactly the thought processes that you go through when comparing a guess in Master to the correct four colour solution.
To keep track of things, we'll start with two 'counters' in our heads.. the number of balls that are the right colour in the right place, and the right colour but in the wrong place. So the first thing to do is
(Right Place)=0 (Wrong Place)=0
Check all balls are guessed (if not, return)
The guess button can be pressed at any time so we should check to see everything is in order before we process the guess.
(Counter)=0 DO (Counter) is increased by one IF guess position (counter) does not have a colour (ie it has a value of zero) Go back into game loop, print up warning message ENDIF UNTIL (counter) equals four
Have we won?
This check is obvious. Have we got the solution correct? And as we're already thinking like a computer, we'll use a DO... UNTIL loop
(Counter)=0 DO (Counter) is increased by one IF guess position (counter) EQUALS colour in solution position (counter) (Right Place) is increased by one ENDIF UNTIL (counter) equals four IF (Right Place) equals four Then the guess is 100% correct, do the victory routine ENDIF
All the above should be pretty obvious, and easy to code (now we've bbroken it down into the small logical steps computer programming requires).
How many balls are the right colour in the right place?
Well, we have the steps already don't we? We can use the code above because the (Right Place variable is going to carry the number of balls we need.
How many balls are the right colour, but in the wrong place?
Stop and think how you do this before we break it into steps. You take the first ball, and see if it matches anything in the solution (ignoring a ball if it is in the same postion (ioe the second guess compared to the second ball in the solution is something you don't do).
(Wrong Place)=0 (Counter One)=0 DO (Counter One) is increased by one (Counter Two)=0 DO (Counter Two) is increased by one IF (Counter One) and (Counter Two) are different IF guess position (Counter One) equals solution position (Counter Two) Add one to (Wrong Place) ENDIF ENDIF UNTIL (Counter Two) equals four UNTIL (Counter One) equals four
So now (Wrong Place) holds the number of balls that are the right colour, but in the wrong place.
Or does it?
PICTURE 1-2-3-4 Guess PICTURE 1-5-5-1 Solution
Have a look at this example. In your head, you know the correct solution is "one in the right place." But if you follow through the steps above, you 'll get "one in the right place and one in the wrong place." Why?
Because the red ball guessed in first place is not only counted in the 'right place' code, it is also counted in the 'wrong place' code by being matched up with forth ball in the solution.
One way to get round this is this simple rule that each guess ball can only be used once, as can each ball in the solution. So to facilitate this, we copy the guess and the solution into temporary arrays. When we match up balls in either the right place or the wrong place, we change the value in the array to something that wouldn't normally be there - we change a guess ball to 0, and a solution ball to 99. This way they cannot be counted again.
This is one example in how 'pseudo-code' can help you pick up bugs and logical flaws before you actually sit in front of the keyboard and start typing.
The Completed Code
So, here we have all the above in OPL.
LOCAL foo%, gnu%,right_place%,wrong_place% LOCAL foobar%(4),guessbar%(4) rem *** Create a copy of the solution so we can remove balls rem *** as there position is noted. Ditto with guess array foo%=0 DO foo%=foo%+1 foobar%(foo%)=solution%(foo%) guessbar%(foo%)=guess%(foo%) UNTIL foo%=4 rem *** check all balls are guessed foo%=0 DO foo%=foo%+1 IF guessbar%(foo%)=0 : giPRINT "Not all spaces filled..." : RETURN : ENDIF UNTIL foo%=4 breakout%=99 : rem *** Eventull incdrement turn counter rem *** right pieces in right place foo%=0 DO foo%=foo%+1 IF guessbar%(foo%)=foobar%(foo%) right_place%=right_place%+1 foobar%(foo%)=0 : rem remove from stemp solution guessbar%(foo%)=99 : rem remove from guess ENDIF UNTIL foo%=4 IF right_place%=4 : rem Victory m_victory: RETURN ENDIF foo%=0 DO foo%=foo%+1 gnu%=0 DO gnu%=gnu%+1 IF guessbar%(foo%)=foobar%(gnu%) wrong_place%=wrong_place%+1 foobar%(gnu%)=0 gnu%=4 ENDIF UNTIL gnu%=4 UNTIL foo%=4giPRINT NUM$(right_place%,1)+" in the right place, "+NUM$(wrong_place%,1)+" in the wrong place" m_showguess:(right_place%,wrong_place%,turn%)
It's not called from the event loop yet (but it shouldn't be that hard to figure out how), and we haven't yet got around to displaying the generated result. All that will be covered in Part 8.
But as with everything, you should be able to get down and do it yourself, so if you want a little challenge over the next fortnight... away you go!
Published by Ewan Spence at 14:56 UTC, August 25th