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.

Abstract Concepts
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:

Boil Kettle

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…

Code Breakdown
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.

Some Counters
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?

The 'Gotcha'

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%=4
giPRINT 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

Section: Articles
Categories: Develop
Platforms: Series 80