rem MindMaster
rem 
rem Author: Jacob Busby
rem Jacob.Busby@hants.gov.uk
rem
rem Known bugs: 
rem
rem  Flicker effort on Go (DIGITS * 3) - 4. Probably 
rem  fixable with double-buffering.
rem 
rem  String manipulation could probably be improved.
rem 
rem Contols: 
rem
rem  Up, Down, Left, Right to choose digits.
rem  Shoulder buttons to move all digits to the 
rem   left/right up/down.
rem  X to confirm Guess.
rem  Use PICK = TRUE for two player version. (See notes)
rem
rem Change History
rem
rem 21/04/01  Initial development complete
rem 23/04/01  Comments added for code clarity.
rem 03/05/01  Shoulder buttons added.

rem Sanity variables: Define Truth/Falsehood

TRUE = 1
FALSE = 0

rem PS2 controller variables

EX = 16384
OH = 8192
UP = 16
DOWN = 64
LEFT = 128
RIGHT = 32
R1 = 2048
L1 = 1024
L2 = 256
R1 = 2048
R2 = 512

rem Control variables
rem
rem DEBUG switches on/off crude debugging mode
rem  (You may also wish to use PICK as well)
rem DIGITS is the number of digits the player has to guess
rem MAXGOES is the number of guesses the player gets
rem PICK switches on/off the ability to pick own digits.
rem  (See below for full details)
rem SCORING switches on/off the score facility. (NB With
rem  the variables DIGITS: 5, MAXGOES: 10, BLACK: 2 and 
rem  WHITE: 1 you get a percentile score) FWIW My high 
rem  score is 77.
rem BLACK is the score awarded for a black peg.
rem WHITE is the score awarded for a white peg.

DEBUG = FALSE
DIGITS = 5
MAXGOES = 10
PICK = FALSE
SCORING = TRUE
BLACK = 2
WHITE = 1

rem Screen variables

XSIZE = 640
YSIZE = 512
X_OFFSET = 100
Y_OFFSET = 100
LINESIZE = 20
FLASHLENGTH = 0.05

rem If you want to pick your own digits use these values 
rem here. You may need to add extra PICK(x) variables, if
rem you use more than five digits. (Effectively, this is
rem the two-player version of the game)
rem
rem NB These variables are only used if PICK is TRUE.

dim PICK(DIGITS)
PICK(1) = 0
PICK(2) = 0
PICK(3) = 0
PICK(4) = 0
PICK(5) = 0

rem That's the global variables done. Let us play!!!

Main()

rem *** GAME ROUTINES ***

rem ------------------------------------------------------
rem main
rem
rem This is the primary routine, from which everything 
rem else is run from.
rem ------------------------------------------------------

sub Main()

  label GameStart
  Set_Up()
  Generate_Hidden_String()
  Go = 0
  Display_Current_Board(Hidden$)  
  while ((Win = FALSE) and (Go < MAXGOES))
    Go = Go + 1
    Determine_Guess()
    Evaluate_Guess()
  wend
  Game_Over()

rem If the player is playing the PS2 then restart.

  if PICK = FALSE then
    goto GameStart
  fi

end sub

rem ------------------------------------------------------
rem Display_Current_Board
rem
rem This routine displays the current contents of the 
rem array board$. It effectively changes the current line
rem in board$ by reading the text$ variable passed to it
rem into board$(x), where x is the second variable passed
rem to it.
rem
rem The board is offset by global variables (X_OFFSET,
rem Y_OFFSET). The size of each line is determined by a  
rem third global variable, LINESIZE.
rem ------------------------------------------------------

sub Display_Current_Board(Text$,Which)

  clear window
  Board$(Which + 1) = Text$
  for Count = 1 to (MAXGOES + 1)
    if Board$(Count) <> "" then
      text X_OFFSET,Y_OFFSET + (Count * LINESIZE),Board$(Count)
    fi
  next Count

end sub

rem ------------------------------------------------------
rem Determine_Guess()
rem
rem This routine allows the player to choose their guess
rem in arcade "Enter your name" fashion.
rem ------------------------------------------------------

sub Determine_Guess()

rem This section is used to prevent "Press X and miss
rem three goes" syndrome.

  while (KeyPress = EX)
    KeyPress = peek("port1")
    wait 0.05
  wend

rem Let the player choose a new value.

  while (KeyPress <> EX)
    KeyPress = peek("port1")
    if (KeyPress = UP)  then
      Guess_No(Current_Digit) = Guess_No(Current_Digit) + 1
      if Guess_No(Current_Digit) > 9 then
        Guess_No(Current_Digit) = 0
      fi
    elsif (KeyPress = DOWN) then
      Guess_No(Current_Digit) = Guess_No(Current_Digit) - 1
      if Guess_No(Current_Digit) < 0 then
        Guess_No(Current_Digit) = 9
      fi
    elsif (KeyPress = LEFT) then
      Current_Digit = Current_Digit - 1
      if Current_Digit < 1 then
        Current_Digit = DIGITS
      fi
    elsif (KeyPress = RIGHT) then
      Current_Digit = Current_Digit + 1
      if Current_Digit > DIGITS then
        Current_Digit = 1
      fi
    elsif (KeyPress = R1) then
      for Digit = Current_Digit to DIGITS
        Guess_No(Digit) = Guess_No(Digit) + 1
        if Guess_No(Digit) > 9 then
          Guess_No(Digit) = 0
        fi
      next Digit
    elsif (KeyPress = L1) then
      for Digit = 1 to Current_Digit
        Guess_No(Digit) = Guess_No(Digit) + 1
        if Guess_No(Digit) > 9 then
          Guess_No(Digit) = 0
        fi
      next Digit
    elsif (KeyPress = R2) then
      for Digit = Current_Digit to DIGITS
        Guess_No(Digit) = Guess_No(Digit) - 1
        if Guess_No(Digit) < 0 then
          Guess_No(Digit) = 9
        fi
      next Digit
    elsif (KeyPress = L2) then
      for Digit = 1 to Current_Digit
        Guess_No(Digit) = Guess_No(Digit) - 1
        if Guess_No(Digit) < 0 then
          Guess_No(Digit) = 9
        fi
      next Digit
    fi
    wait 0.2

rem Flash the current digit, so that the player knows
rem which digit they are altering.

    Guess$ = left$(str$(Go) + "   ",4)
    for Index = 1 to DIGITS
      if Index = Current_Digit then
        Guess$ = Guess$ + " " + " "
      else
        Guess$ = Guess$ + str$(Guess_No(Index)) + " "
      fi
    next Index
    Guess$ = rtrim$(Guess$)
    Display_Current_Board(Guess$, Go)
    wait FLASHLENGTH

rem Display the current guess.

    Guess$ = left$(str$(Go) + "   ",4)
      for Index = 1 to DIGITS
          Guess$ = Guess$ + str$(Guess_No(Index)) + " "
      next Index
      Guess$ = trim$(Guess$)
      Display_Current_Board(Guess$, Go)
  wend

end sub

rem ------------------------------------------------------
rem Evaluate_Guess()
rem
rem This sub-routine determines how much of the players
rem guess is correct. Scoring is defined as White if a  
rem peg is the correct value, but in the wrong location,
rem and Black, if both value and location are correct.
rem Black takes precedence over White. 
rem
rem The score is determined by the global variables, 
rem BLACK and WHITE.
rem ------------------------------------------------------

sub Evaluate_Guess()

  dim Guess_Histogram(10)
  Black = 0
  White = 0

rem Build a blank Histogram for the current guess

  for Index = 1 to 10
    Guess_Histogram(Index) = 0
  next Index

rem Work through each digit of the guess in turn. Add to
rem the guess histogram, then score a black peg if the 
rem value matches a corresponding value in the hidden
rem number. In this instance check that this peg is not 
rem also marked as a white peg. 
rem
rem If the peg is not black compare histograms to see if 
rem it should be marked as a white peg instead.
rem
rem NB Zero is mapped to ten in the histograms.

  for Index = 1 to DIGITS
    Guess_Histogram(Guess_No(Index)) = Guess_Histogram(Guess_No(Index)) + 1
    if Guess_No(Index) = 0 then
      Guess_Histogram(10) = Guess_Histogram(10) + 1
    fi
    if Guess_No(Index) = Hidden_No(Index) then
      Black = Black + 1
      if Guess_No(Index) = 0 then
        if Guess_Histogram(10) > Histogram(10) then
          White = White - 1
        fi
      elsif Guess_Histogram(Guess_No(Index)) > Histogram(Guess_No(Index)) then
        White = White - 1
      fi
    elsif Guess_Histogram(Guess_No(Index)) <= Histogram(Guess_No(Index)) then
      White = White + 1
    elsif Guess_No(Index) = 0 then
      if Guess_Histogram(10) <= Histogram(10) then
        White = White + 1
      fi
    fi 
  next Index

rem In DEBUG mode show the entire histogram.

  if DEBUG = TRUE then
    Guess$ = Guess$ + "   "
    for Index = 1 to 10
      Guess$ = Guess$ + str$(Guess_Histogram(Index)) + " "
    next Index
    Guess$ = rtrim$(Guess$)
  fi
  if Black = DIGITS then
    Win = TRUE
    Score = Score + (BLACK * (DIGITS * (MAXGOES - Go)))
  fi
  Score = Score + (WHITE * White) + (BLACK * Black)

rem Show the number of Black/White pegs.

  Guess$ = Guess$ + "   " + str$(Black) + " " + str$(White)
  if SCORING = TRUE then
    Guess$ = Guess$ + right$("        " + str$(Score),8)
  fi
  Display_Current_Board(Guess$, Go)

end sub

rem ------------------------------------------------------
rem Generate_Hidden_String
rem
rem This routine generates the hidden string, dependent
rem on whether the PICK variables are used or not. In the 
rem latter case random integers are generatd instead.
rem ------------------------------------------------------

sub Generate_Hidden_String()

  dim Histogram(10)
  dim Hidden_No(DIGITS)

rem Build/Blank a histogram (bar-chart) for storing how 
rem many of each digit have been used.

  for Index = 1 to 10
    Histogram(Index) = 0
  next Index

rem Create the hidden string.

  Hidden$ = "-  "
  for digit = 1 to DIGITS
    Random_No = int(ran(10))
    if PICK = TRUE then
      Random_No = PICK(digit)
    fi
    Histogram(Random_No) = Histogram(Random_No) + 1
    if Random_No = 0 then 
      Histogram(10) = Histogram(10) + 1
    fi
    Hidden_No(digit) = Random_No
    if DEBUG = TRUE then
      Hidden$ = Hidden$ + " " + str$(Random_No)
    else  
      Hidden$ = Hidden$ + " X"
    fi
  next digit

rem DEBUG means we display the histogram as well.

  if DEBUG = TRUE then
    Hidden$ = Hidden$ + "   "
    for Index = 1 to 10
      Hidden$ = Hidden$ + str$(Histogram(Index)) + " "
    next Index
  fi
  Hidden$ = trim$(Hidden$)
  Hidden$ = Hidden$ + "   B W"
  if SCORING = TRUE then
    Hidden$ = Hidden$ + "   Score"
  fi

end sub

rem ------------------------------------------------------
rem Game_Over 
rem
rem This routine is run when the game is over.
rem ------------------------------------------------------

sub Game_Over()

rem Determine the text based on whether the player won or
rem not.
  
  if Win = TRUE then
    Game_Over$ = "You won in " + str$(Go) + " turn(s). Press O to continue."
  else
    Game_Over$ = "You lost after " + str$(Go) + " turn(s). Press O to continue."
  fi

rem Reveal the hidden value. (Unnecessary in DEBUG mode)

  if DEBUG = FALSE then
    Hidden$ = "-   "
    for Index = 1 to DIGITS
      Hidden$ = Hidden$ + str$(Hidden_No(Index)) + " "
    next Index
    Hidden$ = trim$(Hidden$) + "   B W"
    if SCORING = TRUE then
      Hidden$ = trim$(Hidden$) + "   Score"
    fi
    Display_Current_Board(Hidden$,0)
  fi

rem Tell the player that they won/lost.

  text X_OFFSET,Y_OFFSET + ((Go + 3) * LINESIZE),Game_Over$

rem Press O to continue...

  KeyPress = peek("port1")
  while (KeyPress <> OH)
    KeyPress = peek("port1")
    wait 0.05
  wend

end sub

rem *** ADMINISTRATION ROUTINES ***

rem ------------------------------------------------------
rem Set_Up
rem
rem A shell routine pointing to each of the set up
rem routines in turn.
rem ------------------------------------------------------

sub Set_Up()

  Set_Up_Variables()
  Set_Up_Screen()

end sub

rem ------------------------------------------------------
rem Set_Up_Variables
rem
rem This routine sets up variable defaults.
rem ------------------------------------------------------

sub Set_Up_Variables()

  Win = FALSE
  Score = 0
  Current_Digit = 1

  dim Guess_No(DIGITS)
  dim Board$(MAXGOES + 1)

rem The first guess will always be made up of zeroes.

  for Index = 1 to DIGITS
    Guess_No(Index) = 0
  next Index 

rem The board string array holds the contents of the
rem current game.

  for Index = 1 to (MAXGOES + 1)
    Board$(Index) = ""
  next Index

end sub

rem ------------------------------------------------------
rem Set_Up_Screen
rem
rem This routine prepares the screen for graphical output.
rem ------------------------------------------------------

sub Set_Up_Screen()

  close window
  open window XSIZE, YSIZE
  buffer = 0
  setdrawbuf buffer
  setrgb 0, 0, 0, 0
  clear window

end sub


