1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: sctictac.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 /* Tic-Tac-Toe program by Steve Chapel schapel@cs.ucsb.edu
35 Uses alpha-beta pruning minimax search to play a "perfect" game.
36 The alpha-beta pruning can be removed, but will increase search time.
37 The heuristic and move ordering in BestMove() can also be removed with
38 an increase in search time. */
44 #include "sctictac.hxx"
47 #include "document.hxx"
51 const Square_Type
ScTicTacToe::Empty
= ' ';
52 const Square_Type
ScTicTacToe::Human
= 'X';
53 const Square_Type
ScTicTacToe::Compi
= 'O';
54 const int ScTicTacToe::Infinity
= 10; /* Higher value than any score */
55 const int ScTicTacToe::Maximum_Moves
= ScTicTacToe_Squares
; /* Maximum moves in a game */
57 /* Array describing the eight combinations of three squares in a row */
58 const int ScTicTacToe::Three_in_a_Row
[ScTicTacToe_Possible_Wins
][3] = {
69 /* Array used in heuristic formula for each move. */
70 const int ScTicTacToe::Heuristic_Array
[4][4] = {
71 { 0, -10, -100, -1000 },
79 ScTicTacToe::ScTicTacToe( ScDocument
* pDocP
, const ScAddress
& rPos
) :
82 aStdOut( "Computer plays O, you play X. " ),
87 ScTicTacToe::ScTicTacToe() :
88 bInitialized( FALSE
),
89 aStdOut( "Computer plays O, you play X. " )
95 /* Return the other player */
96 inline Square_Type
ScTicTacToe::Other(Square_Type Player
)
98 return Player
== Human
? Compi
: Human
;
102 /* Make a move on the board */
103 inline void ScTicTacToe::Play(int Square
, Square_Type Player
)
105 Board
[Square
] = Player
;
109 #ifdef TICTACTOE_STDOUT
111 void ScTicTacToe::GetOutput( ByteString
& rStr
)
117 #else // !TICTACTOE_STDOUT
119 void ScTicTacToe::GetOutput( String
& rStr
)
121 rStr
= String( aStdOut
, gsl_getSystemTextEncoding() );
125 #endif // TICTACTOE_STDOUT
128 /* Clear the board */
129 void ScTicTacToe::Initialize( BOOL bHumanFirst
)
132 aPlayer
= (bHumanFirst
? Human
: Compi
);
134 for (int i
= 0; i
< ScTicTacToe_Squares
; i
++)
139 /* If a player has won, return the winner. If the game is a tie,
140 return 'C' (for cat). If the game is not over, return Empty. */
141 Square_Type
ScTicTacToe::Winner()
144 for (i
= 0; i
< ScTicTacToe_Possible_Wins
; i
++)
146 Square_Type Possible_Winner
= Board
[Three_in_a_Row
[i
][0]];
147 if (Possible_Winner
!= Empty
&&
148 Possible_Winner
== Board
[Three_in_a_Row
[i
][1]] &&
149 Possible_Winner
== Board
[Three_in_a_Row
[i
][2]])
150 return Possible_Winner
;
153 for (i
= 0; i
< ScTicTacToe_Squares
; i
++)
155 if (Board
[i
] == Empty
)
163 /* Return a heuristic used to determine the order in which the
164 children of a node are searched */
165 int ScTicTacToe::Evaluate(Square_Type Player
)
169 for (i
= 0; i
< ScTicTacToe_Possible_Wins
; i
++)
172 int Players
= 0, Others
= 0;
173 for (j
= 0; j
< 3; j
++)
175 Square_Type Piece
= Board
[Three_in_a_Row
[i
][j
]];
178 else if (Piece
== Other(Player
))
181 Heuristic
+= Heuristic_Array
[Players
][Others
];
187 /* Return the score of the best move found for a board
188 The square to move to is returned in *Square */
189 int ScTicTacToe::BestMove(Square_Type Player
, int *Square
,
190 int Move_Nbr
, int Alpha
, int Beta
)
192 int Best_Square
= -1;
195 Move_Heuristic_Type Move_Heuristic
[ScTicTacToe_Squares
];
199 /* Find the heuristic for each move and sort moves in descending order */
200 for (i
= 0; i
< ScTicTacToe_Squares
; i
++)
202 if (Board
[i
] == Empty
)
207 Heuristic
= Evaluate(Player
);
209 for (j
= Moves
-1; j
>= 0 &&
210 Move_Heuristic
[j
].Heuristic
< Heuristic
; j
--)
212 Move_Heuristic
[j
+ 1].Heuristic
= Move_Heuristic
[j
].Heuristic
;
213 Move_Heuristic
[j
+ 1].Square
= Move_Heuristic
[j
].Square
;
215 Move_Heuristic
[j
+ 1].Heuristic
= Heuristic
;
216 Move_Heuristic
[j
+ 1].Square
= i
;
221 for (i
= 0; i
< Moves
; i
++)
224 int Sq
= Move_Heuristic
[i
].Square
;
227 /* Make a move and get its score */
232 Score
= (Maximum_Moves
+ 1) - Move_Nbr
;
234 Score
= Move_Nbr
- (Maximum_Moves
+ 1);
238 Score
= BestMove(Other(Player
), Square
, Move_Nbr
+ 1,
243 /* Perform alpha-beta pruning */
251 else if (Score
> Alpha
)
264 else if (Score
< Beta
)
271 *Square
= Best_Square
;
279 /* Provide an English description of the score returned by BestMove */
280 void ScTicTacToe::Describe(int Score
)
283 aStdOut
+= "You have a guaranteed win. ";
285 aStdOut
+= "I can guarantee a tie. ";
288 aStdOut
+= "I have a guaranteed win by move ";
289 aStdOut
+= ByteString::CreateFromInt32( Maximum_Moves
- Score
+ 1 );
295 /* Have the human or the computer move */
296 void ScTicTacToe::Move( int& Square
)
298 if (aPlayer
== Compi
)
301 Describe(BestMove(aPlayer
, &Square
, nMove
, -Infinity
, Infinity
));
302 aStdOut
+= ByteString::CreateFromInt32( Total_Nodes
);
303 aStdOut
+= " nodes examined. ";
304 Play(Square
, aPlayer
);
306 aStdOut
+= ByteString::CreateFromInt32( nMove
);
307 aStdOut
+= " - O moves to ";
308 aStdOut
+= ByteString::CreateFromInt32( Square
+ 1 );
310 aPlayer
= Other( aPlayer
);
315 if ( Square
< 0 || Square
>= ScTicTacToe_Squares
316 || Board
[Square
] != Empty
)
320 Play(Square
, aPlayer
);
321 aPlayer
= Other( aPlayer
);
329 Square_Type
ScTicTacToe::TryMove( int& Square
)
334 Square_Type W
= Winner();
338 #ifdef TICTACTOE_STDOUT
341 puts( aStdOut
.GetBuffer() );
349 if ( aPlayer
== Human
)
356 aStdOut
+= static_cast< char >(W
);
360 aStdOut
+= "It's a tie.";
366 void ScTicTacToe::PromptHuman()
369 aStdOut
+= ByteString::CreateFromInt32( nMove
);
370 aStdOut
+= " - What is X's move?";
376 void ScTicTacToe::DrawPos( int nSquare
, const String
& rStr
)
378 pDoc
->SetString( sal::static_int_cast
<SCCOL
>( aPos
.Col()+(nSquare
%3) ),
379 sal::static_int_cast
<SCROW
>( aPos
.Row()+(nSquare
/3) ), aPos
.Tab(), rStr
);
383 void ScTicTacToe::DrawBoard()
386 for ( USHORT j
= 0; j
< ScTicTacToe_Squares
; j
++ )
394 // -1 == Fehler/Redraw, 0 == keine Aenderung, >0 == UserMoveSquare+1
395 int ScTicTacToe::GetStatus()
406 for ( USHORT j
= 0; j
< ScTicTacToe_Squares
; j
++ )
408 pDoc
->GetString( nCol
+(j
%3), nRow
+(j
/3), nTab
, aStr
);
411 if ( Board
[j
] != Empty
)
412 return -1; // wo was sein muss muss was sein
417 if ( aStr
.GetChar(0) != Board
[j
] )
419 if ( Board
[j
] != Empty
)
420 return -1; // bestehendes ueberschrieben
421 // bei erstem Move hat Human angefangen
423 return -1; // mehr als eine Aenderung
434 Square_Type
ScTicTacToe::CalcMove()
436 Square_Type W
= Winner();
437 int nStat
= GetStatus();
438 if ( nStat
|| (W
== Empty
&& aPlayer
== Compi
) )
440 if ( nStat
== -1 || (nStat
> 0 && aPlayer
== Compi
) )
442 if ( W
== Empty
&& aPlayer
== Human
)
446 int nSquare
= --nStat
;
447 W
= TryMove( nStat
);
449 DrawPos( nSquare
, String( ' ' ) );
451 DrawPos( nStat
, String( Human
) );
456 if ( W
== Empty
&& aPlayer
== Compi
)
458 W
= TryMove( nStat
); // ComputerMove, nStat egal
459 DrawPos( nStat
, String( Compi
) );
462 else if ( W
== Empty
&& aPlayer
== Human
)
467 #endif // TICTACTOE_SC
470 #ifdef TICTACTOE_STDOUT
471 /* Print the board */
472 void ScTicTacToe::Print()
475 for (i
= 0; i
< ScTicTacToe_Squares
; i
+= 3)
478 printf("---+---+---\n");
479 printf(" %c | %c | %c \n", Board
[i
], Board
[i
+ 1], Board
[i
+ 2]);
485 /* Play a game of tic-tac-toe */
486 void ScTicTacToe::Game()
491 int Square
= (aPlayer
== Compi
? 0 : -1);
492 Square_Type W
= Winner();
496 W
= TryMove( Square
);
499 if ( aPlayer
== Human
)
502 Print(); // empty board already printed if human moves first
505 puts( aStdOut
.GetBuffer() );
507 scanf("%d", &Square
);
509 W
= TryMove( Square
);
510 } while ( Square
== -1 );
515 puts( aStdOut
.GetBuffer() );
518 #endif // TICTACTOE_STDOUT
521 #ifdef TICTACTOE_MAIN
526 printf("Welcome to Tic-Tac-Toe!\n\n");
527 printf("Here is the board numbering:\n");
528 printf(" 1 | 2 | 3\n");
529 printf("---+---+---\n");
530 printf(" 4 | 5 | 6\n");
531 printf("---+---+---\n");
532 printf(" 7 | 8 | 9\n");
534 // printf("Computer plays X, you play O.\n");
538 aTTT
.GetOutput( aStr
);
539 puts( aStr
.GetBuffer() );
543 printf("\nDo you want to move first? ");
545 aTTT
.Initialize( toupper(Answer
[0]) == 'Y' );
547 printf("\nDo you want to play again? ");
549 } while (toupper(Answer
[0]) == 'Y');
553 #endif // TICTACTOE_MAIN