Universität Ulm,
Fakultät für Mathematik und Wirtschaftswissenschaften,
SAI
SS 99 || Ferienprojekt zu Allgemeine Informatik II || Puzzle Library
MODULE Puzzle
MODULE Puzzle;
IMPORT Iterators, NetIO, Objects, PersistentObjects, Services, Streams;
CONST boardsize = 7;
CONST
player1 = 0; player2 = 1; empty = 2;
TYPE
Piece = SHORTINT; (* player1..empty *)
Player = SHORTINT; (* player1 or player2 *)
Board = ARRAY boardsize, boardsize OF Piece;
TYPE
Move = POINTER TO MoveRec;
MoveRec =
RECORD
(PersistentObjects.ObjectRec)
fromx, fromy, tox, toy: SHORTINT; (* 0 .. boardsize-1 *)
END;
Situation = POINTER TO SituationRec;
SituationRec =
RECORD
(PersistentObjects.ObjectRec)
board: Board;
onturn: Player;
nofpieces: ARRAY 2 OF INTEGER;
END;
VAR
moveType, situationType: Services.Type;
PROCEDURE ^CreateMove(VAR move: Move; fromx, fromy, tox, toy: SHORTINT);
PROCEDURE ^Valid(situation: Situation; move: Move; player: Player): BOOLEAN;
PROCEDURE InternalCreateMove(VAR object: PersistentObjects.Object);
VAR
move: Move;
BEGIN
NEW(move);
PersistentObjects.Init(move, moveType);
object := move;
END InternalCreateMove;
PROCEDURE ReadMove(s: Streams.Stream;
object: PersistentObjects.Object) : BOOLEAN;
BEGIN
WITH object: Move DO
RETURN NetIO.ReadShortInt(s, object.fromx) &
NetIO.ReadShortInt(s, object.fromy) &
NetIO.ReadShortInt(s, object.tox) &
NetIO.ReadShortInt(s, object.toy)
END;
END ReadMove;
PROCEDURE WriteMove(s: Streams.Stream;
object: PersistentObjects.Object) : BOOLEAN;
BEGIN
WITH object: Move DO
RETURN NetIO.WriteShortInt(s, object.fromx) &
NetIO.WriteShortInt(s, object.fromy) &
NetIO.WriteShortInt(s, object.tox) &
NetIO.WriteShortInt(s, object.toy)
END;
END WriteMove;
PROCEDURE InternalCreateSituation(VAR object: PersistentObjects.Object);
VAR situation: Situation;
BEGIN
NEW(situation); PersistentObjects.Init(situation, situationType);
object := situation;
END InternalCreateSituation;
PROCEDURE ReadSituation(s: Streams.Stream;
object: PersistentObjects.Object) : BOOLEAN;
VAR
row, col: INTEGER;
BEGIN
WITH object: Situation DO
row := 0;
WHILE row < boardsize DO
col := 0;
WHILE col < boardsize DO
IF ~NetIO.ReadShortInt(s, object.board[row, col]) THEN
RETURN FALSE
END;
INC(col);
END;
INC(row);
END;
RETURN NetIO.ReadShortInt(s, object.onturn) &
NetIO.ReadInteger(s, object.nofpieces[player1]) &
NetIO.ReadInteger(s, object.nofpieces[player2])
END;
END ReadSituation;
PROCEDURE WriteSituation(s: Streams.Stream;
object: PersistentObjects.Object) : BOOLEAN;
VAR
row, col: INTEGER;
BEGIN
WITH object: Situation DO
row := 0;
WHILE row < boardsize DO
col := 0;
WHILE col < boardsize DO
IF ~NetIO.WriteShortInt(s, object.board[row, col]) THEN
RETURN FALSE
END;
INC(col);
END;
INC(row);
END;
RETURN NetIO.WriteShortInt(s, object.onturn) &
NetIO.WriteInteger(s, object.nofpieces[player1]) &
NetIO.WriteInteger(s, object.nofpieces[player2])
END;
END WriteSituation;
PROCEDURE InitBoard(VAR board: Board);
VAR
row, col: SHORTINT;
BEGIN
row := 0;
WHILE row < boardsize DO
col := 0;
WHILE col < boardsize DO
board[row, col] := empty;
INC(col);
END;
INC(row);
END;
board[0, 0] := player1;
board[0, boardsize-1] := player2;
board[boardsize-1, 0] := player2;
board[boardsize-1, boardsize-1] := player1;
END InitBoard;
PROCEDURE MovePossible(situation: Situation; player: Player): BOOLEAN;
VAR
move: Move;
i, j, k, l: SHORTINT;
BEGIN
CreateMove(move, 0, 0, 0, 0);
i := 0;
WHILE i < boardsize DO
j := 0;
WHILE j < boardsize DO
IF situation.board[i, j] = player THEN
move.fromx := i; move.fromy := j;
k := -2;
WHILE k < 3 DO
l := -2;
WHILE l < 3 DO
move.tox := move.fromx + k; move.toy := move.fromy + l;
IF (move.tox >= 0) & (move.tox < boardsize) &
(move.toy >= 0) & (move.toy < boardsize) &
(situation.board[move.tox, move.toy] = empty) THEN
RETURN TRUE
END;
INC(l);
END;
INC(k);
END;
END;
INC(j);
END;
INC(i);
END;
RETURN FALSE
END MovePossible;
PROCEDURE OtherPlayer(player: Player) : Player;
BEGIN
CASE player OF
| player1: RETURN player2
| player2: RETURN player1
END;
END OtherPlayer;
PROCEDURE NextPlayer(situation: Situation; player: Player) : Player;
BEGIN
IF MovePossible(situation, OtherPlayer(player)) THEN
RETURN OtherPlayer(player)
ELSE
RETURN player
END;
END NextPlayer;
PROCEDURE Init;
VAR
pif: PersistentObjects.Interface;
BEGIN
NEW(pif);
pif.create := InternalCreateMove;
pif.read := ReadMove; pif.write := WriteMove;
pif.createAndRead := NIL;
PersistentObjects.RegisterType(moveType, "Puzzle.Move", "", pif);
NEW(pif);
pif.create := InternalCreateSituation;
pif.read := ReadSituation; pif.write := WriteSituation;
pif.createAndRead := NIL;
PersistentObjects.RegisterType(situationType,
"Puzzle.Situation", "", pif);
END Init;
(* === exported procedures ============================================== *)
PROCEDURE CreateMove(VAR move: Move; fromx, fromy, tox, toy: SHORTINT);
BEGIN
InternalCreateMove(move);
move.fromx := fromx; move.fromy := fromy;
move.tox := tox; move.toy := toy;
END CreateMove;
PROCEDURE CreateSituation(VAR situation: Situation);
BEGIN
InternalCreateSituation(situation);
InitBoard(situation.board);
situation.onturn := player1;
situation.nofpieces[player1] := 2;
situation.nofpieces[player2] := 2;
END CreateSituation;
PROCEDURE CopySituation(VAR situation: Situation; orig: Situation);
BEGIN
InternalCreateSituation(situation);
situation.board := orig.board;
situation.onturn := orig.onturn;
situation.nofpieces := orig.nofpieces;
END CopySituation;
PROCEDURE GetBoard(situation: Situation; VAR board: Board);
BEGIN
board := situation.board;
END GetBoard;
PROCEDURE NumberOfPieces(situation: Situation; player: Player) : INTEGER;
BEGIN
RETURN situation.nofpieces[player]
END NumberOfPieces;
PROCEDURE Finished(situation: Situation) : BOOLEAN;
BEGIN
RETURN (situation.nofpieces[player1] = 0) OR
(situation.nofpieces[player2] = 0) OR
(situation.nofpieces[player1] + situation.nofpieces[player2] =
boardsize * boardsize)
END Finished;
PROCEDURE Winner(situation: Situation) : Piece;
(* empty means nobody has won *)
BEGIN
ASSERT(Finished(situation));
IF situation.nofpieces[player1] > situation.nofpieces[player2] THEN
RETURN player1
ELSIF situation.nofpieces[player2] > situation.nofpieces[player1] THEN
RETURN player2
ELSE
RETURN empty
END;
END Winner;
PROCEDURE Onturn(situation: Situation) : Player;
BEGIN
ASSERT(~Finished(situation));
RETURN situation.onturn
END Onturn;
PROCEDURE Valid(situation: Situation; move: Move; player: Player): BOOLEAN;
BEGIN
RETURN ~Finished(situation) & (situation.onturn = player) &
(move.fromx >=0) & (move.fromx < boardsize) &
(move.fromy >=0) & (move.fromy < boardsize) &
(move.tox >=0) & (move.tox < boardsize) &
(move.toy >=0) & (move.toy < boardsize) &
(ABS(move.fromx - move.tox) <= 2) &
(ABS(move.fromy - move.toy) <= 2) &
(ABS(move.fromx - move.tox) + ABS(move.fromy - move.toy) > 0) &
(situation.board[move.fromx, move.fromy] = player) &
(situation.board[move.tox, move.toy] = empty);
END Valid;
PROCEDURE IterateMoves(it: Iterators.Iterator; situation: Objects.Object;
player: Player);
VAR
i, j, k, l: SHORTINT;
move: Move;
boardcnt: ARRAY boardsize, boardsize OF SHORTINT;
PROCEDURE Yield;
VAR
m: Move;
BEGIN
CreateMove(m, move.fromx, move.fromy, move.tox, move.toy);
Iterators.Yield(it, m);
END Yield;
BEGIN (* IterateMoves *)
IF situation = NIL THEN RETURN END;
WITH situation: Situation DO
CreateMove(move, 0, 0, 0, 0);
i := 0;
WHILE i < boardsize DO
j := 0;
WHILE j < boardsize DO
boardcnt[i, j] := 0;
INC(j);
END;
INC(i);
END;
i := 0;
WHILE i < boardsize DO
j := 0;
WHILE j < boardsize DO
IF situation.board[i, j] = player THEN
move.fromx := i; move.fromy := j;
k := -2;
WHILE k < 3 DO
l := -2;
WHILE l < 3 DO
move.tox := move.fromx + k; move.toy := move.fromy + l;
IF Valid(situation, move, player) THEN
IF (ABS(move.tox - move.fromx) <= 1) &
(ABS(move.toy - move.fromy) <= 1) THEN
INC(boardcnt[move.tox, move.toy]);
IF boardcnt[move.tox, move.toy] = 1 THEN
Yield;
END;
ELSE
Yield;
END;
END;
INC(l);
END;
INC(k);
END;
END; (* IF *)
INC(j);
END;
INC(i);
END;
END;
END IterateMoves;
PROCEDURE CreateIterator(VAR it: Iterators.Iterator; situation: Situation;
player: Player);
BEGIN
Iterators.Create(it, IterateMoves, situation, player);
END CreateIterator;
PROCEDURE MakeMove(situation: Situation; move: Move; player: Player);
VAR x, y: SHORTINT; ok: BOOLEAN; other: Player;
BEGIN
ok := Valid(situation, move, player); ASSERT(ok);
other := OtherPlayer(player);
situation.board[move.tox, move.toy] := player;
INC(situation.nofpieces[player]);
IF (ABS(move.fromx - move.tox) = 2) OR
(ABS(move.fromy - move.toy) = 2) THEN
situation.board[move.fromx, move.fromy] := empty;
DEC(situation.nofpieces[player]);
END;
x := -1;
WHILE x < 2 DO
y := -1;
WHILE y < 2 DO
IF (move.tox + x >= 0) & (move.tox + x < boardsize) &
(move.toy + y >= 0) & (move.toy + y < boardsize) &
(situation.board[move.tox + x, move.toy + y] = other) THEN
DEC(situation.nofpieces[other]);
situation.board[move.tox + x, move.toy + y] := player;
INC(situation.nofpieces[player]);
END;
INC(y);
END;
INC(x);
END;
situation.onturn := NextPlayer(situation, player);
END MakeMove;
BEGIN
Init;
END Puzzle.
SS 99 || Ferienprojekt zu Allgemeine Informatik II || Puzzle Library
Andreas Borchert, 26. Juli 1999