Tic-tac-toe: Difference between revisions
Content added Content deleted
m (The name of the game is spelled "noughts and crosses". While the spelling "naught" took over as the general synonym for "nothing", "nought" is still used to refer to zero as a figure (0).) |
No edit summary |
||
Line 4,445: | Line 4,445: | ||
You win!</pre> |
You win!</pre> |
||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
This is a GUI, event driven version of the game. The game board is a "TStringGrid" component, which has a grid that can hold and display characters. In this case, the grid displays Xs and Ox using a large font. |
|||
When the user clicks on the grid, this generates an event that is handled by the program. The mouse click coordinates are converted to a cell coordinates and a large X is placed in the box. At this point program tests if this resulted in a win. Next it calculate the computer's response and it checks to see if the computer won. The process is repeated until there is a winner or a draw. |
|||
<syntaxhighlight lang="Delphi"> |
|||
{Array contain possiple winning lines} |
|||
type TWinLine = array [0..3-1] of TPoint; |
|||
var WinLines: array [0..8-1] of TWinLine = ( |
|||
((X:0; Y:0), (X:1; Y:0), (X:2; Y:0)), |
|||
((X:0; Y:1), (X:1; Y:1), (X:2; Y:1)), |
|||
((X:0; Y:2), (X:1; Y:2), (X:2; Y:2)), |
|||
((X:0; Y:0), (X:0; Y:1), (X:0; Y:2)), |
|||
((X:1; Y:0), (X:1; Y:1), (X:1; Y:2)), |
|||
((X:2; Y:0), (X:2; Y:1), (X:2; Y:2)), |
|||
((X:0; Y:0), (X:1; Y:1), (X:2; Y:2)), |
|||
((X:2; Y:0), (X:1; Y:1), (X:0; Y:2)) |
|||
); |
|||
{Array containing all characters in a line} |
|||
type TCellArray = array [0..3-1] of char; |
|||
var WinLineInx: integer; |
|||
var GameOver: boolean; |
|||
procedure ClearGrid; |
|||
{Clear TTT Grid by setting all cells to a space} |
|||
var X,Y: integer; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
for Y:=0 to GameGrid.RowCount-1 do |
|||
for X:=0 to GameGrid.ColCount-1 do GameGrid.Cells[X,Y]:=' '; |
|||
WinLineInx:=-1; |
|||
Status1Dis.Caption:=''; |
|||
GameOver:=False; |
|||
end; |
|||
end; |
|||
function FirstEmptyCell(var P: TPoint): boolean; |
|||
{Find first empty grid i.e. the one containing a space} |
|||
{Returns false if there are no empty cells (a tie game)} |
|||
var X,Y: integer; |
|||
begin |
|||
Result:=True; |
|||
with TicTacToeDlg do |
|||
begin |
|||
{Iterate through all cells in array} |
|||
for Y:=0 to GameGrid.RowCount-1 do |
|||
for X:=0 to GameGrid.ColCount-1 do |
|||
if GameGrid.Cells[X,Y]=' ' then |
|||
begin |
|||
P.X:=X; P.Y:=Y; |
|||
Exit; |
|||
end; |
|||
end; |
|||
Result:=False; |
|||
end; |
|||
procedure GetLineChars(Inx: integer; var CA: TCellArray); |
|||
{Get all the characters in a specific win line} |
|||
var P1,P2,P3: TPoint; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
{Get cell position of specific win line} |
|||
P1:=WinLines[Inx,0]; |
|||
P2:=WinLines[Inx,1]; |
|||
P3:=WinLines[Inx,2]; |
|||
{Get the characters from each and put in array} |
|||
CA[0]:=GameGrid.Cells[P1.X,P1.Y][1]; |
|||
CA[1]:=GameGrid.Cells[P2.X,P2.Y][1]; |
|||
CA[2]:=GameGrid.Cells[P3.X,P3.Y][1]; |
|||
end; |
|||
end; |
|||
function IsWinner(var Inx: integer; var C: char): boolean; |
|||
{Test if the specified line is a winner} |
|||
{Return index of line and the char of winner} |
|||
var I,J: integer; |
|||
var CA: TCellArray; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
Result:=False; |
|||
{Go through all winning patterns} |
|||
for J:=0 to High(WinLines) do |
|||
begin |
|||
{Get one winning pattern} |
|||
GetLineChars(J,CA); |
|||
{Look for line that has the same char in all three places} |
|||
if (CA[0]<>' ') and (CA[0]=CA[1]) and (CA[0]=CA[2]) then |
|||
begin |
|||
Result:=True; |
|||
Inx:=J; |
|||
C:=CA[0]; |
|||
end; |
|||
end; |
|||
end; |
|||
end; |
|||
procedure DrawWinLine(Inx: integer); |
|||
{Draw line through winning squares} |
|||
var Line: TWinLine; |
|||
var C1,C2: TPoint; |
|||
var P1,P2: TPoint; |
|||
var W2,H2: integer; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
{Offset to center of cell} |
|||
W2:=GameGrid.ColWidths[0] div 2; |
|||
H2:=GameGrid.RowHeights[0] div 2; |
|||
{Get winning pattern of lines} |
|||
Line:=WinLines[Inx]; |
|||
{Get beginning and ending cell of win} |
|||
C1:=Line[0]; C2:=Line[2]; |
|||
{Convert to screen coordinates} |
|||
P1.X:=C1.X * GameGrid.ColWidths[0] + W2; |
|||
P1.Y:=C1.Y * GameGrid.RowHeights[0] + H2; |
|||
P2.X:=C2.X * GameGrid.ColWidths[0] + W2; |
|||
P2.Y:=C2.Y * GameGrid.RowHeights[0] + H2; |
|||
{Set line attributes} |
|||
GameGrid.Canvas.Pen.Color:=clRed; |
|||
GameGrid.Canvas.Pen.Width:=5; |
|||
{Draw line} |
|||
GameGrid.Canvas.MoveTo(P1.X,P1.Y); |
|||
GameGrid.Canvas.LineTo(P2.X,P2.Y); |
|||
end; |
|||
end; |
|||
procedure DoBestComputerMove; |
|||
{Analyze game board and execute the best computer move} |
|||
var I,J,Inx: integer; |
|||
var CA: TCellArray; |
|||
var P: TPoint; |
|||
function UrgentMove(CA: TCellArray; var EmptyInx: integer): boolean; |
|||
{Test row, column or diagonal for an urgent move} |
|||
{This would be either an immediate win or immediate loss} |
|||
{Returns True if there is an urgent move and the index of location to respond} |
|||
var I: integer; |
|||
var OCnt,XCnt,EmptyCnt: integer; |
|||
begin |
|||
Result:=False; |
|||
OCnt:=0; XCnt:=0; |
|||
EmptyCnt:=0; EmptyInx:=-1; |
|||
{Count number of Xs, Os or Spaces in line} |
|||
for I:=0 to High(CA) do |
|||
begin |
|||
case CA[I] of |
|||
'O': Inc(OCnt); |
|||
'X': Inc(XCnt); |
|||
' ': |
|||
begin |
|||
Inc(EmptyCnt); |
|||
if EmptyCnt=1 then EmptyInx:=I; |
|||
end; |
|||
end; |
|||
end; |
|||
{Look for pattern of one empty and two Xs or two Os} |
|||
{Which means it's one move away from a win} |
|||
Result:=(EmptyCnt=1) and ((OCnt=2) or (XCnt=2)); |
|||
end; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
{Look for urgent moves in all patterns of wins} |
|||
for J:=0 to High(WinLines) do |
|||
begin |
|||
{Get a winning pattern of chars} |
|||
GetLineChars(J,CA); |
|||
if UrgentMove(CA,Inx) then |
|||
begin |
|||
{Urgent move found - take it} |
|||
P:=WinLines[J,Inx]; |
|||
GameGrid.Cells[P.X,P.Y]:='O'; |
|||
exit; |
|||
end; |
|||
end; |
|||
{No urgent moves, so use first empty} |
|||
{If there is no empty, the game is stalemated} |
|||
if FirstEmptyCell(P) then GameGrid.Cells[P.X,P.Y]:='O'; |
|||
end; |
|||
end; |
|||
function TestWin: boolean; |
|||
{Test if last move resulted in win/draw} |
|||
var Inx: integer; var C: char; |
|||
var P: TPoint; |
|||
var S: string; |
|||
begin |
|||
Result:=True; |
|||
{Test if somebody won} |
|||
if IsWinner(Inx,C) then |
|||
begin |
|||
WinLineInx:=Inx; |
|||
TicTacToeDlg.GameGrid.Invalidate; |
|||
if C='O' then S:=' Computer Won!!' else S:=' You Won!!'; |
|||
TicTacToeDlg.Status1Dis.Caption:=S; |
|||
GameOver:=True; |
|||
exit; |
|||
end; |
|||
{Test if game is a draw} |
|||
if not FirstEmptyCell(P) then |
|||
begin |
|||
TicTacToeDlg.Status1Dis.Caption:=' Game is Draw.'; |
|||
GameOver:=True; |
|||
exit; |
|||
end; |
|||
Result:=False; |
|||
end; |
|||
procedure PlayTicTacToe; |
|||
{Show TicTacToe dialog box} |
|||
begin |
|||
ClearGrid; |
|||
TicTacToeDlg.ShowModal |
|||
end; |
|||
procedure TTicTacToeDlg.GameGridMouseDown(Sender: TObject; |
|||
Button: TMouseButton; Shift: TShiftState; X, Y: Integer); |
|||
{Handle user click on TicTacToe grid} |
|||
var Row,Col: integer; |
|||
begin |
|||
with TicTacToeDlg do |
|||
begin |
|||
if GameOver then exit;; |
|||
{Convert Mouse X,Y to Grid row and column} |
|||
GameGrid.MouseToCell(X,Y,Col,Row); |
|||
{Mark user's selection by placing X in the cell} |
|||
if GameGrid.Cells[Col,Row]=' 'then |
|||
GameGrid.Cells[Col,Row]:='X'; |
|||
{Did this result in a win? } |
|||
if TestWin then exit; |
|||
{Get computer's response} |
|||
DoBestComputerMove; |
|||
{Did computer win} |
|||
TestWin; |
|||
end; |
|||
end; |
|||
procedure TTicTacToeDlg.ReplayBtnClick(Sender: TObject); |
|||
begin |
|||
ClearGrid; |
|||
end; |
|||
procedure TTicTacToeDlg.GameGridDrawCell(Sender: TObject; ACol, |
|||
ARow: Integer; Rect: TRect; State: TGridDrawState); |
|||
{Draw winner line on top of Xs and Os} |
|||
begin |
|||
if WinLineInx>=0 then DrawWinLine(WinLineInx); |
|||
end; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Elapsed Time: 19.126 Sec. |
|||
</pre> |
|||
=={{header|EasyLang}}== |
=={{header|EasyLang}}== |