Shall We Play a Game?
Changing the Rules
by David Schultz and Greg Krimer
(our key Gamesman Scheme architects --Dan)

Overview

Introduction

This week (2001-11-13,14) we'll be wrapping up the standard game by writing generate-moves and primitive.By the next checkoff, you should have the standard and misére versions of your game playable on Gamesman. To do this, you need to know how to use set-rule!, get-rule and add-menu-entry! to configure game rules.

In general, please consult our two example modules, mttt.scm and m1210.scm, to see how we modify the rules for a real game

get-rule, set-rule!, and add-menu-entry!

Gamesman defines three procedures that allow you to change the rules of your basic game and turn it into a completely new game with a (very possibly) different strategy. The three procedures are:

Think of get-rule and set-rule! as accessing a table keyed by the names of your rules. For example, to lookup the value stored under the key standard-game, you'd do:

(get-rule 'standard-game)

What get-rule returns is based on what you last put there using set-rule!. Watch:

> (set-rule! 'standard-game #t)
okay
> (get-rule 'standard-game)
#t
> (set-rule! 'standard-game 'bla-bla-bla-bla)
okay
> (get-rule 'standard-game)
bla-bla-bla-bla

You should use get-rule and set-rule! to create procedures which

  1. know how to change some rule of the game and
  2. know how to display the current setting for that rule.

Every game template already comes with such procedures for the standard-game rule. They are called display-standard-misére and toggle-standard-misére. When a rule has only two allowable settings (a standard game or a misére game), a procedure that toggles these settings is particularly appropriate. Most of the user-defined rules you are asked to implement are not so simple; many -- though not all -- of them will require you to read input from the user (the size of the game board, for instance, or the name of a chess-piece). A simple template to follow in this case, would be:

(define (change-my-rule)
  (display "Enter a new value for my-rule: ") ;; trailing space important!
  (let ((value (read)))
    (if (valid-my-rule-value? value)
        (set-rule! my-rule value)
        (begin (display "Bad value for my-rule: ")
               (display value)
               (newline)
               (change-my-rule)))))

This is probably the simplest you could do. Feel free to make it much more fancy. Your top priority is, of course, to get your new rules to work; your second priority is to make changing the rules as simple and intuitive as possible.

When you're done writing the procedure to display the current setting of a rule and the procedure to change it, you ll want to use add-menu-entry! to stick them into the right menu in Gamesman so that you have access to them when playing your game. There are two menus in Gamesman: a game-specific menu and a game-independent menu. The game-specific menu comes first and is titled "name-of-your-game OPTIONS". It is this menu where your rule-changing options will appear. The other game-independent menu, titled "PLAY OPTIONS", allows you to decide if the computer or human goes first and set the names of any human players.

You should not need to change the game-independent menu.

Let's examine a call to add-menu-entry! that already appears in each game template:

(add-menu-entry! "T" display-standard-misére toggle-standard-misére)

In each menu, entries are keyed by a unique (to that menu) letter. That letter is the first argument to add-menu-entry!. It must be an uppercase String. The management is not responsible for the consequences of adding menu entries keyed by a letter that belongs to another entry in this menu. Don t forget that "H" is already taken by the "Help" entry, "Q" is already taken by the "Quit" entry, and "S" is taken by the "Solve and play your game" entry.

The second argument is the procedure that displays the value of the standard-game rule. The procedure that is the second argument to add-menu-entry! gets called each time the menu is printed to display the current setting of a particular rule. How does display-standard-misére know what to display? Figure it out!

The third argument is the procedure that gets called to change the standard-game rule. It is invoked by Gamesman every time the user types "T". toggle-standard-misére is a toggling procedure; it does not read any input from the user or print anything on the screen. It simply issues a call to set-rule!, and the next time this menu is printed display-standard-misére simply prints the new setting.

To get your rule to appear in the game-specific menu, you must use add-menu-entry!. You also need to set the default value of every rule, so that when you first play the game some setting exists for every rule. This is accomplished by a single call to set-rule!.

You now know how to use set-rule!, get-rule and add-menu-entry!. This, of course, is not sufficient to actually play your game with modified rules. We must, additionally, modify some of the five basic procedures (print-help, do-move, print-position, generate-moves and primitive) so that they do something a little different depending on the rules currently in play, i.e., depending on the particular settings of the rules.

The big 5 : print-help, do-move, print-position, generate-moves and primitive

Take print-help, for instance. It must display a help message appropriate to the rules in play. If we re playing misére Tic-Tac-Toe, we don t want print-help telling us that the way to win the game is to get three-in-a-row. It should always tell us the objective given the current rules. If you re playing Kayles with a ball of size 5, print-help should tell us that a player can knock down up to five contiguous pins at a time. If you ve also created a rule in Kayles that allows a player to remove non-contiguous pins, then print-help should tell us that a player can knock down up to five pins -- contiguous or not. It is up to you to decide what someone playing your game must know about the current rules -- and to put that information in print-help. Don't make print-help too long, or it'll get boring; print-help should print a concise message describing how to move (the "ON YOUR TURN..." field) and how to win (the "THE OBJECTIVE IS..." field) that does not omit any truly important details.

What other functions need to change to accommodate the new rules? Well, it depends on the rules. One procedure that probably should not change, however, is do-move; do-move should -- to borrow the Nike slogan -- just do it. That s why we asked you not to build error checks into do-move.

Should print-position change? It's up to you. It is nice, however, to see the setting of some important rules (such as the standard-game rule) printed along with the position to serve as a reminder. We don t want to think we're playing a standard game -- and find out it was in fact misére only when Deep Blue wins it.

Speaking of the standard-game rule, what is the one procedure that needs to do something different when playing a misére game than when playing a standard game?

Compulsory and User-defined rules...no loopy games!

For each of the five games, we are asking you to implement some compulsory rules. See the Final Project Game Theory Introduction Lecture page to find out what the compulsory rules are for your game. Additionally, you should come up with at least one of your own rules for your game. There are a lot of different rules you can come up with (a magical losing square, going off the board, randomly generated starting positions, etc.), but due to the limitations of the Scheme Gamesman, there are some you may not implement. Any game which, as part of the rules, might visit the same position twice in the process of playing a game, is called loopy. Loopy games are not handled in CS3 's Gamesman scheme code.

Thus, your rules may never make the game loopy.

Said another way, you may not add rules that somehow undo the game since this will cause Gamesman to go into an infinite loop when trying to solve it. For instance, allowing pieces to move backwards in Toads and Frogs, allowing the un-poisoning of squares in Poison, allowing the placing of pins or pieces (as opposed to simply removing) in Kayles and TacTix, and allowing the Snake to shrink all produce loopy games and all will cause Gamesman to run forever.

CHANGE-INITIAL-POSITION and DISPLAY-INITIAL-POSITION

The templates of the two games with a variable board size -- Kayles and Toads and Frogs -- include two procedures CHANGE-INITIAL-POSITION and DISPLAY-INITIAL-POSITION that take care of reading an intial position from the user. These procedures do a substantial amount of the error-checking and come with their own help messages. Feel free to modify these two procedures, or not to touch them at all -- they already do everything that you should need as far as reading and setting an initial-position. It is up to you, however, to modify as many of the Big Five procedures as neccesary to accomodate play on a variable-sized linear board. The boards for Snake, TacTix and Poison do not change size from 3x3; when the compulsory rules ask you to "Enter an arbitrary initial position", we mean for you to enter a position within a3x3 board. Now, that said, there's nothing stopping the Scheme Jedis from changing the board to be able to be larger than 3x3 and changing the hash functions as well.


Questions, comments, bugs with gamesman? Email cs3-gamesman@nim.cs.berkeley.edu