1 /* gomoku - 5 in a row game Author: ? */
3 /* This program plays a very old Japanese game called GO-MOKU,
4 perhaps better known as 5-in-line. The game is played on
5 a board with 19 x 19 squares, and the object of the game is
6 to get 5 stones in a row.
15 /* Size of the board */
18 /* Importance of attack (1..16) */
19 #define AttackFactor 4
21 /* Value of having 0, 1,2,3,4 or 5 pieces in line */
22 int Weight
[7] = {0, 0, 4, 20, 100, 500, 0};
35 char PieceChar
[Nought
+ 1] = {' ', 'X', '0'};
37 int Board
[SIZE
+ 1][SIZE
+ 1];/* The board */
38 int Player
; /* The player whose move is next */
39 int TotalLines
; /* The number of Empty lines left */
40 int GameWon
; /* Set if one of the players has won */
42 int Line
[4][SIZE
+ 1][SIZE
+ 1][Nought
+ 1];
44 /* Value of each square for each player */
45 int Value
[SIZE
+ 1][SIZE
+ 1][Nought
+ 1];
47 int X
, Y
; /* Move coordinates */
48 char Command
; /* Command from keyboard */
49 int AutoPlay
= FALSE
; /* The program plays against itself */
51 _PROTOTYPE(void Initialize
, (void));
52 _PROTOTYPE(int Abort
, (char *s
));
53 _PROTOTYPE(void WriteLetters
, (void));
54 _PROTOTYPE(void WriteLine
, (int j
, int *s
));
55 _PROTOTYPE(void WriteBoard
, (int N
, int *Top
, int *Middle
, int *Bottom
));
56 _PROTOTYPE(void SetUpScreen
, (void));
57 _PROTOTYPE(void GotoSquare
, (int x
, int y
));
58 _PROTOTYPE(void PrintMove
, (int Piece
, int X
, int Y
));
59 _PROTOTYPE(void ClearMove
, (void));
60 _PROTOTYPE(void PrintMsg
, (char *Str
));
61 _PROTOTYPE(void ClearMsg
, (void));
62 _PROTOTYPE(void WriteCommand
, (char *S
));
63 _PROTOTYPE(void ResetGame
, (int FirstGame
));
64 _PROTOTYPE(int OpponentColor
, (int Player
));
65 _PROTOTYPE(void BlinkRow
, (int X
, int Y
, int Dx
, int Dy
, int Piece
));
66 _PROTOTYPE(void BlinkWinner
, (int Piece
, int X
, int Y
, int WinningLine
));
67 _PROTOTYPE(int Random
, (int x
));
68 _PROTOTYPE(void Add
, (int *Num
));
69 _PROTOTYPE(void Update
, (int Lin
[], int Valu
[], int Opponent
));
70 _PROTOTYPE(void MakeMove
, (int X
, int Y
));
71 _PROTOTYPE(int GameOver
, (void));
72 _PROTOTYPE(void FindMove
, (int *X
, int *Y
));
73 _PROTOTYPE(char GetChar
, (void));
74 _PROTOTYPE(void ReadCommand
, (int X
, int Y
, char *Command
));
75 _PROTOTYPE(void InterpretCommand
, (int Command
));
76 _PROTOTYPE(void PlayerMove
, (void));
77 _PROTOTYPE(void ProgramMove
, (void));
78 _PROTOTYPE(int main
, (void));
80 /* Set terminal to raw mode. */
83 srand(getpid() + 13); /* Initialize the random seed with our pid */
90 /* Reset terminal and exit from the program. */
100 /* Set up the screen ----------------------------------------------- */
102 /* Write the letters */
109 for (i
= 1; i
<= SIZE
; i
++) printw(" %c", 'A' + i
- 1);
113 /* Write one line of the board */
122 for (i
= 2; i
<= SIZE
- 1; i
++) {
128 printw(" %-2d\n", j
);
131 /* Print the Empty board and the border */
132 void WriteBoard(N
, Top
, Middle
, Bottom
)
134 int *Top
, *Middle
, *Bottom
;
141 for (j
= N
- 1; j
>= 2; j
--) WriteLine(j
, Middle
);
142 WriteLine(1, Bottom
);
146 /* Sets up the screen with an Empty board */
149 int top
[4], middle
[4], bottom
[4];
151 top
[0] = ACS_ULCORNER
;
154 top
[3] = ACS_URCORNER
;
156 middle
[0] = ACS_LTEE
;
157 middle
[1] = ACS_HLINE
;
158 middle
[2] = ACS_PLUS
;
159 middle
[3] = ACS_RTEE
;
161 bottom
[0] = ACS_LLCORNER
;
162 bottom
[1] = ACS_HLINE
;
163 bottom
[2] = ACS_BTEE
;
164 bottom
[3] = ACS_LRCORNER
;
166 WriteBoard(SIZE
, top
, middle
, bottom
);
169 /* Show moves ----------------------------------------------- */
171 void GotoSquare(x
, y
)
174 move(SIZE
+ 2 - y
, 1 + x
* 2);
178 void PrintMove(Piece
, X
, Y
)
183 printw("%c %c %d", PieceChar
[Piece
], 'A' + X
- 1, Y
);
186 addch(PieceChar
[Piece
]);
191 /* Clears the line where a move is displayed */
198 /* Message handling ---------------------------------------------- */
200 /* Prints a message */
204 mvprintw(23, 1, "%s", Str
);
207 /* Clears the message about the winner */
214 /* Highlights the first letter of S */
224 /* Display the board ----------------------------------------------- */
226 /* Resets global variables to start a new game */
227 void ResetGame(FirstGame
)
236 addstr("G O M O K U");
238 WriteCommand("Newgame ");
239 WriteCommand("Quit ");
241 WriteCommand("Auto");
243 WriteCommand("Play");
245 WriteCommand("Hint");
247 WriteCommand("Left, ");
248 WriteCommand("Right, ");
250 WriteCommand("Up, ");
251 WriteCommand("Down");
256 WriteCommand(" NOTE: Use Num Lock & arrows");
258 mvaddstr(14, 49, "7 8 9");
259 mvaddch(15, 52, ACS_UARROW
);
260 mvaddch(16, 49, '4');
262 mvaddch(16, 54, ACS_RARROW
);
264 mvaddch(17, 52, ACS_DARROW
);
265 mvaddstr(18, 49, "1 2 3");
273 for (I
= 1; I
<= SIZE
; I
++) for (J
= 1; J
<= SIZE
; J
++) {
275 for (C
= Cross
; C
<= Nought
; C
++) {
277 for (D
= 0; D
<= 3; D
++) Line
[D
][I
][J
][C
] = 0;
283 /* Total number of lines */
284 TotalLines
= 2 * 2 * (SIZE
* (SIZE
- 4) + (SIZE
- 4) * (SIZE
- 4));
288 int OpponentColor(Player
)
297 /* Blink the row of 5 stones */
298 void BlinkRow(X
, Y
, Dx
, Dy
, Piece
)
299 int X
, Y
, Dx
, Dy
, Piece
;
304 for (I
= 1; I
<= 5; I
++) {
306 addch(PieceChar
[Piece
]);
313 /* Prints the 5 winning stones in blinking color */
314 void BlinkWinner(Piece
, X
, Y
, WinningLine
)
315 int Piece
, X
, Y
, WinningLine
;
317 /* Used to store the position of the winning move */
319 /* Change in X and Y */
322 /* Display winning move */
323 PrintMove(Piece
, X
, Y
);
324 /* Preserve winning position */
327 switch (WinningLine
) {
357 /* Go to topmost, leftmost */
358 while (Board
[X
+ Dx
][Y
+ Dy
] != Empty
&& Board
[X
+ Dx
][Y
+ Dy
] == Piece
) {
362 BlinkRow(X
, Y
, Dx
, Dy
, Piece
);
363 /* Restore winning position */
366 /* Go back to winning square */
370 /* Functions for playing a game -------------------------------- */
375 return((rand() / 19) % x
);
378 /* Adds one to the number of pieces in a line */
382 /* Adds one to the number. */
384 /* If it is the first piece in the line, then the opponent cannot use
386 if (*Num
== 1) TotalLines
= TotalLines
- 1;
387 /* The game is won if there are 5 in line. */
388 if (*Num
== 5) GameWon
= TRUE
;
391 /* Updates the value of a square for each player, taking into
392 account that player has placed an extra piece in the square.
393 The value of a square in a usable line is Weight[Lin[Player]+1]
394 where Lin[Player] is the number of pieces already placed
396 void Update(Lin
, Valu
, Opponent
)
401 /* If the opponent has no pieces in the line, then simply update the
402 * value for player */
403 if (Lin
[Opponent
] == 0)
404 Valu
[Player
] += Weight
[Lin
[Player
] + 1] - Weight
[Lin
[Player
]];
406 /* If it is the first piece in the line, then the line is
407 * spoiled for the opponent */
408 if (Lin
[Player
] == 1) Valu
[Opponent
] -= Weight
[Lin
[Opponent
] + 1];
411 /* Performs the move X,Y for player, and updates the global variables
412 (Board, Line, Value, Player, GameWon, TotalLines and the screen) */
418 int K
, L
, WinningLine
;
421 Opponent
= OpponentColor(Player
);
424 /* Each square of the board is part of 20 different lines. The adds
425 * one to the number of pieces in each of these lines. Then it
426 * updates the value for each of the 5 squares in each of the 20
427 * lines. Finally Board is updated, and the move is printed on the
430 /* Horizontal lines, from left to right */
431 for (K
= 0; K
<= 4; K
++) {
432 X1
= X
- K
; /* Calculate starting point */
434 if ((1 <= X1
) && (X1
<= SIZE
- 4)) { /* Check starting point */
435 Add(&Line
[0][X1
][Y1
][Player
]); /* Add one to line */
436 if (GameWon
&& (WinningLine
== Null
)) /* Save winning line */
438 for (L
= 0; L
<= 4; L
++) /* Update value for the
439 * 5 squares in the line */
440 Update(Line
[0][X1
][Y1
], Value
[X1
+ L
][Y1
], Opponent
);
444 for (K
= 0; K
<= 4; K
++) { /* Diagonal lines, from lower left to
448 if ((1 <= X1
) && (X1
<= SIZE
- 4) &&
449 (1 <= Y1
) && (Y1
<= SIZE
- 4)) {
450 Add(&Line
[1][X1
][Y1
][Player
]);
451 if (GameWon
&& (WinningLine
== Null
)) /* Save winning line */
452 WinningLine
= DownLeft
;
453 for (L
= 0; L
<= 4; L
++)
454 Update(Line
[1][X1
][Y1
], Value
[X1
+ L
][Y1
+ L
], Opponent
);
458 for (K
= 0; K
<= 4; K
++) { /* Diagonal lines, down right to upper left */
461 if ((5 <= X1
) && (X1
<= SIZE
) &&
462 (1 <= Y1
) && (Y1
<= SIZE
- 4)) {
463 Add(&Line
[3][X1
][Y1
][Player
]);
464 if (GameWon
&& (WinningLine
== Null
)) /* Save winning line */
465 WinningLine
= DownRight
;
466 for (L
= 0; L
<= 4; L
++)
467 Update(Line
[3][X1
][Y1
], Value
[X1
- L
][Y1
+ L
], Opponent
);
471 for (K
= 0; K
<= 4; K
++) { /* Vertical lines, from down to up */
474 if ((1 <= Y1
) && (Y1
<= SIZE
- 4)) {
475 Add(&Line
[2][X1
][Y1
][Player
]);
476 if (GameWon
&& (WinningLine
== Null
)) /* Save winning line */
478 for (L
= 0; L
<= 4; L
++)
479 Update(Line
[2][X1
][Y1
], Value
[X1
][Y1
+ L
], Opponent
);
483 Board
[X
][Y
] = Player
; /* Place piece in board */
485 BlinkWinner(Player
, X
, Y
, WinningLine
);
487 PrintMove(Player
, X
, Y
);/* Print move on screen */
488 Player
= Opponent
; /* The opponent is next to move */
492 /* A game is over if one of the players have
493 won, or if there are no more Empty lines */
495 return(GameWon
|| (TotalLines
<= 0));
498 /* Finds a move X,Y for player, simply by picking the one with the
507 Opponent
= OpponentColor(Player
);
509 /* If no square has a high value then pick the one in the middle */
512 if (Board
[*X
][*Y
] == Empty
) Max
= 4;
513 /* The evaluation for a square is simply the value of the square for
514 * the player (attack points) plus the value for the opponent
515 * (defense points). Attack is more important than defense, since it
516 * is better to get 5 in line yourself than to prevent the op- ponent
517 * from getting it. */
519 /* For all Empty squares */
520 for (I
= 1; I
<= SIZE
; I
++) for (J
= 1; J
<= SIZE
; J
++)
521 if (Board
[I
][J
] == Empty
) {
522 /* Calculate evaluation */
523 Valu
= Value
[I
][J
][Player
] * (16 + AttackFactor
) / 16 + Value
[I
][J
][Opponent
] + Random(4);
524 /* Pick move with highest value */
534 /* Get a character from the keyboard */
540 if (c
== '\033') { /* arrow key */
541 if ((c
= getch()) == '[') {
544 case 'A': c
= 'U'; break;
545 case 'B': c
= 'D'; break;
546 case 'C': c
= 'R'; break;
547 case 'D': c
= 'L'; break;
562 /* Reads in a valid command character */
563 void ReadCommand(X
, Y
, Command
)
571 GotoSquare(X
, Y
); /* Goto square */
573 *Command
= GetChar(); /* Read from keyboard */
575 case '\n': /* '\n', '\r' or space means place a */
579 break; /* stone at the cursor position */
596 case '8': *Command
= 'U'; break;
597 case '2': *Command
= 'D'; break;
598 case '4': *Command
= 'L'; break;
599 case '6': *Command
= 'R'; break;
605 ValidCommand
= FALSE
;
609 } while (!ValidCommand
);
612 void InterpretCommand(Command
)
618 case 'N':{ /* Start new game */
619 ResetGame(FALSE
); /* ResetGame but only redraw
627 break; /* Give the user a hint */
629 X
= (X
+ SIZE
- 2) % SIZE
+ 1;
635 Y
= (Y
+ SIZE
- 2) % SIZE
+ 1;
641 if ((X
== 1) || (Y
== SIZE
)) { /* Move diagonally *//* t
642 * owards upper left */
652 case '9':{ /* Move diagonally */
653 if (X
== SIZE
) {/* toward upper right */
656 } else if (Y
== SIZE
) {
665 case '1':{ /* Move diagonally */
666 if (Y
== 1) { /* toward lower left */
678 case '3':{ /* Move diagonally */
679 if ((X
== SIZE
) || (Y
== 1)) { /* toward lower right */
691 break; /* Auto play mode */
693 } /* InterpretCommand */
696 /* Enter and make a move */
698 if (Board
[X
][Y
] == Empty
) {
700 if (GameWon
) PrintMsg("Congratulations, You won!");
707 /* Find and perform programs move */
712 if ((Command
!= 'Q') && (!GameWon
)) PrintMsg("Tie game!");
716 if (GameWon
) PrintMsg("I won!");
725 ResetGame(TRUE
); /* ResetGame and draw the entire screen */
727 X
= (SIZE
+ 1) / 2; /* Set starting position to */
728 Y
= X
; /* the middle of the board */
730 ReadCommand(X
, Y
, &Command
);
732 if (Command
!= 'Q') Command
= 'N';
733 InterpretCommand(Command
);
734 if (Command
== 'E') PlayerMove();
735 if (Command
== 'P' || Command
== 'A') ProgramMove();
736 } while (Command
!= 'Q');