The entire file is located at trunk/ProjectFortress/demos/tictactoe.fss.
This module is called tictactoe. It must be placed in a file named tictactoe.fss.
List.{...} lets me use the List object and File.{...} lets me use FileReadStream. The {...} means "import everything exported by that module and used in my module". If I just wanted to be able to create a List object I could have said "import List.{List}", but I used the <| |> syntax below which is exported by List.
This program is runnable so it must export the Executable api.
These are global immutable variables. boardSize is the width and height of the board, a usual game of tic-tac-toe has a size of 3.
IllegalMove is an exception thrown when a move is chosen that does not exist on the board. This exception accepts one parameter, n, which is supposed to be the move that was tried. There is no constructor method; the object itself serves as a constructor. The parameter to the object declares a field; it can be used directly in the body as you can see in the toString() method. There is no operator between the string "Illegal move at" and n because there is implicitly an operator already between them: the whitespace operator, otherwise known as juxtaposition. It would be similar to saying
juxtaposition("Illegal move at", n)
Which is defined in trunk/Library/FortressLibrary.fss as converting n to a string and concatenating the two strings.
Now I have defined my own juxtaposition operator when a number is in front of a string. I want to do something other than concatenting the strings. This definition of juxtaposition will concatenate the string x times to itself. This is done using the BIG || operator which applies the || operator to each element produced by the expression y for each element in the set n <- 0#x.
This function provides a way to index a list. It first computes the sub-list from 0 to n+1 via the take() method (which is exclusive, so we really have 0 - n) and then chooses the right most element which is the nth element. right() returns a Maybe object which could either be something or nothing, but I know its something so I call get() to get the actual value.
This creates a new list but replacing the index-th element with val. The || operator concatenates Lists together.
This defines a function that takes one argument, id. case is like a switch statement from C or Java which tests the expression after the case keyword, in this case id, against each expression before the => and if they are equal will return the expression after the =>.
Defines a new object which accepts one argument, rows.
The easiest way to build up a string is to make a mutable string. A mutable variable can be declared by prepending the name with the keyword var. The type of the variable always must be given as well, in this case :String gives the type String to the variable.
I will loop through the entire board in order. Without the seq() functions, the order would be unpredictable. seq() is short for sequential.
If the position on the board is empty (has a value of 0) then the board should show the number that represents the position. Juxtaposition of y and boardSize denotes multiplication, corresponding to standard mathematical notation. The list of rows is zero indexed so I have to add 1 to it to produce a number between 1 and 9.
Otherwise I want to show an X or an O depending on what the value of the element is.
Between each column a | character is inserted.
After each row a line of ----'s is inserted and a newline.
Finally all is returned.
Just so there is no confusion here, (x = "no") is a string comparison between x and "no", not assigning x to the value "no".
A label is much like a goto, but really it is a continuation that can only be invoked once. No matter what the program is doing inside the label, if the label is invoked then control jumps to the end of the label block with a given value.
The play function accepts the latest board as input and returns nothing.
The player gets to go first, by virtue of the board.move(pos,1), where pos is the position read in from standard input. After the player moves, the computer (AI) picks a move and finally the board resulting from the AI move is sent back to the play method.
If the player selects a position not on the board then IllegalMove? will be thrown by board.move(). In that case the current move can just be restarted with the original board. If the AI could also generate IllegalMove?'s then the player might get two turns in a row, but the AI does not generate illegal moves (because there are no bugs in the program :) ).
run is the main method of an executable program. Indeed it is the only function exported by the Executable api.

