General
PHP Tic Tac Toe Game Source
May 11, 2015
0

Here’s the source to my Tic Tac Toe PHP game. I originally wrote this in Perl before porting it to PHP. It’s quite primitive (load a new page every time you make a move). It has a computer opponent with a decent AI, implementing Minimax (see http://en.wikipedia.org/wiki/Minimax for more) algorithm.

You can see it running here.

Download as zip (including images).

Source:

<?php
/*####################################################################
# A simple tic tac toe game
# by F. Permadi,
# created February 1997
# (C) F. Permadi (permadi@permadi.com)
# Updated April 8, 2000
# Ported from Perl to PHP.
####################################################################3

# Global Settings
*/
// Used for iframing the app
$FRAMED=isset($_GET['framed']) ? $_GET['framed']:"0"; 

$IMAGE_PATH="/fpcgi/tictac/";
$EXIT_FILENAME="/fpcgi/index.html";
//#$SOURCE_FILENAME="${PROG_PATH}tictac.pl";
$SCRIPT_FILENAME="index.php";
$NEWGAME_FILENAME=$SCRIPT_FILENAME."?framed=".$FRAMED."&do=new";
//#arbitary large number for indicating winning score
$WIN_SCORE=900;
// used to form links
$PARAM="";
$GRID_VALUES=array();

$NEWGAME_FILENAME=$SCRIPT_FILENAME."?do=new&framed=".$FRAMED;

// make sure the app is run from a valid domain
$server = $_SERVER['SERVER_NAME'];
$validServer = "permadi.com";
if ($server == $validServer)
{
	global $X_WINS, $O_WINS, $ACTION;

	list($usec, $sec) = explode(' ', microtime());
	srand((float) $sec + ((float) $usec * 100000));

	$ACTION=isset($_GET['do']) ? $_GET['do']:"new"; 

	print_header();

	if ($ACTION == "new")
	{
		#indicate that noone is winning yet
		$X_WINS=$O_WINS=0;
		create_new_game();
		create_href_params();
		draw_game();
		print "<BR><BR>Try to win against the computer player in this web version of the popular tic-tac-toe game.";
		print "<br>You may move first and you know that usually that means you should be able to win in a tic-tac-toe game if you move first.";
	}

	if ($ACTION == "play")
	{
		decode_game();
		check_win();
		#make sure computer doesn't move if player already winds
		if ($O_WINS==0)
		{
			move();
		}
		create_href_params();
		check_win();
		check_stalemate();
		draw_game();
		print "<BR><BR>Try to win against the computer player in this web version of the popular tic-tac-toe game.";
	}	

	print_footer();
}

##########################################################################
##########################################################################
function create_new_game()
{
	global $GRID_VALUES;

	#assign an empty tile for each grid
	for ($i=0; $i<9; $i++)
	{
		$GRID_VALUES[$i]='E';
	}
}

##########################################################################
##########################################################################
function decode_game()
{
	global $GRID_VALUES;
    $posParam=isset($_GET['pos']) ? $_GET['pos']:"";

	#decode the value on each square
	$pos = split(":", $posParam);
	for ($i=0; $i<9; $i++)
	{
		$GRID_VALUES[$i]=$pos[$i];
	}

	#determine which square is clicked and change its value
	$clickedRow=isset($_GET['row']) ? $_GET['row'] : 0;
	$clickedCol=isset($_GET['col']) ? $_GET['col'] : 0;
	$GRID_VALUES[$clickedRow*3+$clickedCol]='O';
}

##########################################################################
# the $PARAM variable will be appended to clickable tiles so that the game knows the values of each grid.
##########################################################################
function create_href_params()
{
	global $PARAM, $GRID_VALUES, $FRAMED;

	$PARAM='framed='.$FRAMED.'&pos=';

	for ($i=0; $i<9; $i++)
	{
		if ($GRID_VALUES[$i] == 'X')
		{
			$PARAM.='X:';
		}
		else if ($GRID_VALUES[$i] == 'O')
		{
			$PARAM.='O:';
		}
		else
		{
			$PARAM.='E:';
		}
	}
}

##########################################################################
##########################################################################
function move()
{
	global $WIN_SCORE, $GRID_VALUES;

	$mustBlockRow=-1;
	$mustBlockCol=-1;
	$bestRow=-1;
	$bestCol=-1;
	$winningRow=-1;
	$winningCol=-1;
	$bestScore=-1;

	for ($row=0; $row<3; $row++)
	{
		for ($col=0; $col<3; $col++)
		{
			// check empty grids and see how we fare we we put a piece on it
			if ($GRID_VALUES[$row*3+$col] == 'E')
			{
				//see whether enemy will win or not if we don't pick this pos
				if (how_good($row, $col,'O','X')>=$WIN_SCORE)
				{
					$mustBlockCol=$col;
					$mustBlockRow=$row;
					$bestRow=$row;
					$bestCol=$col;
				}
				//how good is this position?
				$newScore=how_good($row, $col, 'X','O');
				//echo "newScore=".$newScore;
				//if better than previous one, select
				// (also select randomly if we got a same score, so that
				// the computer doesn't always pick the same move)
				if (($bestScore<$newScore) || (($bestScore==$newScore) && rand(0,100)>50))
				{
					$bestScore=$newScore;
					$bestRow=$row;
					$bestCol=$col;
				}

			}
		}
	}

	//if we have a winning position, pick it
	if ($bestScore>=$WIN_SCORE)
	{
		$GRID_VALUES[$bestRow*3+$bestCol]='X';
	}
	//if we have a position that must be blocked or else the enemy will win, block it
	else if ($mustBlockCol!=-1)
	{
		$GRID_VALUES[$mustBlockRow*3+$mustBlockCol]='X';
	}
	//else, pick the best move
	else if ($GRID_VALUES[$bestRow*3+$bestCol] == 'E')
	{
		$GRID_VALUES[$bestRow*3+$bestCol]='X';
	}
}

##########################################################################
# Find out how good a move is
##########################################################################
function how_good($row, $col, $good, $bad)
{
	global $WIN_SCORE, $GRID_VALUES;
	$score=0;

	//check horizontal
	$occupied=0;
	$totalOccupied=0;
	for ($c=0; $c<3; $c++)
	{
		//straight line from this position is blocked
		if ($GRID_VALUES[$row*3+$c] == $bad)
		{
			$occupied=0;
			$score++;
			break;
		}
		else
		{
			#straight line from this position is not blocked
			if ($c==2)
			{
				$score++;
			}
			if ($GRID_VALUES[$row*3+$c] == $good)
			{
				$occupied++;
			}
		}
	}

	if ($occupied==2)
	{
		$totalOccupied+=$WIN_SCORE;
	}
	$totalOccupied+=$occupied;

	#check vertical
	$occupied=0;
	for ($r=0; $r<3; $r++)
	{
		if ($GRID_VALUES[$r*3+$col] == $bad)
		{
			$occupied=0;
			$score++;
			break;
		}
		else
		{
			if ($r==2)
			{
				$score++;
			}
			if ($GRID_VALUES[$r*3+$col] == $good)
			{
				$occupied++;
			}
		}
	}

	if ($occupied==2)
	{
		$totalOccupied+=$WIN_SCORE;
	}
	$totalOccupied+=$occupied;

	#check diagonal left-right
	if ($row==$col)
	{
		$occupied=0;
		for ($i=0; $i<3; $i++)
		{
			if ($GRID_VALUES[$i*3+$i] == $bad)
			{
				$occupied=0;
				$score++;
				break;
			}
			else
			{
				if ($i==2)
				{
					$score++;
				}

				if ($GRID_VALUES[$i*3+$i] == $good)
				{
					$occupied++;
				}
			}
		}
		if ($occupied==2)
		{
			$totalOccupied+=$WIN_SCORE;
		}
	}

	$totalOccupied+=$occupied;

	#check diagonal right-left
	if ($row==2-$col)
	{
		$occupied=0;
		for ($i=0; $i<3; $i++)
		{
			if ($GRID_VALUES[$i*3+(2-$i)] == $bad)
			{
				$occupied=0;
				$score++;
				break;
			}
			else
			{
				if ($i==2)
				{
					$score++;
				}

				if ($GRID_VALUES[$i*3+(2-$i)] == $good)
				{
					$occupied++;
				}
			}
		}
		if ($occupied==2)
		{
			$totalOccupied+=$WIN_SCORE;
		}
	}

	$totalOccupied+=$occupied;
	$score+=$totalOccupied;
	#print "<H1>$good, r: $row, c: $col, s:$score </H1>";
	return $score;
}

##########################################################################
##########################################################################
function check_win()
{
	global $X_WINS, $O_WINS, $GRID_VALUES;
	$X_WINS=0;
	$O_WINS=0;

	#check horizontals
	for ($row=0; $row<3; $row++)
	{
		$xOccupied=0;
		$oOccupied=0;
		for ($col=0; $col<3; $col++)
		{
			if ($GRID_VALUES[$row*3+$col] == 'X')
			{
				$xOccupied++;
			}
			else if ($GRID_VALUES[$row*3+$col] == 'O')
			{
				$oOccupied++;
			}
		}
		if (check_win2($xOccupied, $oOccupied))
		{
			return;
		}
	}

	#check verticals
	for ($col=0; $col<3; $col++)
	{
		$xOccupied=0;
		$oOccupied=0;
		for ($row=0; $row<3; $row++)
		{
			if ($GRID_VALUES[$row*3+$col] == 'X')
			{
				$xOccupied++;
			}
			else if ($GRID_VALUES[$row*3+$col] == 'O')
			{
				$oOccupied++;
			}
		}
		if (check_win2($xOccupied, $oOccupied))
		{
			return;
		}
	}

	#check diagonals left-right
	$xOccupied=0;
	$oOccupied=0;

	for ($col=0; $col<3; $col++)
	{
		if ($GRID_VALUES[$col*3+$col] == 'X')
		{
			$xOccupied++;
		}
		else if ($GRID_VALUES[$col*3+$col] == 'O')
		{
			$oOccupied++;
		}
	}
	if (check_win2($xOccupied, $oOccupied))
	{
		return;
	}

	#check diagonals right-left
	$xOccupied=0;
	$oOccupied=0;

	for ($col=0; $col<3; $col++)
	{
		if ($GRID_VALUES[$col*3+(2-$col)] == 'X')
		{
			$xOccupied++;
		}
		else if ($GRID_VALUES[$col*3+(2-$col)] == 'O')
		{
			$oOccupied++;
		}
	}
	check_win2($xOccupied, $oOccupied);
}

##########################################################################
##########################################################################
function check_win2($xOccupied, $oOccupied)
{
	global $X_WINS, $O_WINS;

	#since this function may be called multiple times, make sure it doesn't spit out
	# the same messages
	if ($X_WINS ==1 || $O_WINS==1)
	{
		0;
	}
	else if ($xOccupied>=3)
	{
		print "<H3 style='color:red;'>COMPUTER WINS</BLINK></H3>\n";
		$X_WINS=1;
	}
	else if ($oOccupied>=3)
	{
		print "<H3 style='color:green;'>YOU WIN</BLINK></H3>\n";
		$O_WINS=1;
	}
}

##########################################################################
##########################################################################
function check_stalemate()
{
	global $X_WINS, $O_WINS, $GRID_VALUES;

	if ($X_WINS==0 && $O_WINS==0)
	{

		#check for stalemate
		$stalemate=1;
		for ($o=0; $o<9; $o++)
		{
			if ($GRID_VALUES[$o] == 'E')
			{
				$stalemate=0;
			}
		}
		if ($stalemate==1)
		{
			print "<H3><BLINK>STALEMATE</BLINK></H3>";
		}
	}
}

##########################################################################
##########################################################################
function draw_game()
{
	global $GRID_VALUES, $IMAGE_PATH, $SCRIPT_FILENAME, $X_WINS, $O_WINS, $PARAM;

	print "<P><P>";

	print "<CENTER><TABLE BGCOLOR=\"#FFFFFF\" BORDER=\"0\" CELLPADDING=\"0\" CELLSPACING=\"0\"><TR>";
	for ($row=0; $row<3; $row++)
	{
		for ($col=0; $col<3; $col++)
		{
			#'action' indicates the next action to do
			#'moved' indicates the index of thece that is being moved
			#'moves' indicates the total #of moves that has been performed
			print "<TD NOWRAP>";
			if ($GRID_VALUES[$row*3+$col] == 'E' && $X_WINS==0 && $O_WINS==0)
			{
				print "<A HREF=$SCRIPT_FILENAME?do=play&$PARAM&row=$row&col=$col>";
			}
			print "<IMG WIDTH=\"75\" HEIGHT=\"75\" BORDER=\"1\" SRC=\"$IMAGE_PATH";
			if ($GRID_VALUES[$row*3+$col] == 'O')
			{
				#image name is "o.gif"
				print "o";
			}
			else if ($GRID_VALUES[$row*3+$col] == 'X')
			{
				#image name is "x.gif"
				print "x";
			}
			else
			{
				#image name is "e.gif"
				print "e";
			}
			print ".gif\"></A>";
		}
		print "<TR>";
	}
	print "</TABLE>\n";
	print "<BR><BR>PLAYER IS  <IMG ALIGN=ABSMIDDLE WIDTH=\"20\" HEIGHT=\"20\" SRC=\"".$IMAGE_PATH."o.gif\">\n<BR>Click a tile to move.\n";
}

##########################################################################
##########################################################################
function print_header()
{
	global $FRAMED;
	# Print the required HTTPD Content header
    print "<HTML><HEAD><TITLE>Web and Mobile Tic Tac Toe Game</TITLE>\n
		<meta name=\"Keywords\" content=\"Web games, mobile games, WAP games, online fun, tic tac toe, tic-tac-toe\">\n
		<meta name=\"Description\" content=\"Tic Tac Toe for Web and Mobile\"></HEAD>		\n";
	if ($FRAMED=="0")
	{
		echo '<LINK REL=stylesheet TYPE=\"text/css\" HREF=\"/style/main.css\">\n';
		print "<BODY BGCOLOR=\"#FFFFFF\" TEXT=\"#000000\" ALINK=\"#000088\" VLINK=\"#000088\" LINK=\"#000088\"><CENTER>\n";
	}
	else
	{
		print "<BODY><CENTER>\n";
	}

	if ($FRAMED=="0")
	{
		print "<p>\n
		<TABLE CELLSPACING=\"0\" CELLPADDING=\"35\"
		bordercolor=\"#000000\" ALIGN=\"CENTER\" BORDER=\"1\" BGCOLOR=\"#0099CC\" style='width:100%;'>
		<TR><TD ALIGN=\"CENTER\">
		<IMG SRC=\"/fpcgi/images/header.gif\">
		</TD></TR><TR><TD ALIGN=\"CENTER\" BGCOLOR=\"#FFFFFF\">\n
                <H2>CGI Tic Tac Toe</H2>\n";
	}
}

##########################################################################
##########################################################################
function print_footer()
{
	global $NEWGAME_FILENAME, $EXIT_FILENAME, $SCRIPT_FILENAME, $FRAMED;

	print "<HR><BR><A  CLASS=\"darklink\" HREF=\"$NEWGAME_FILENAME\">RESTART</A>\n";
	print "<HR><BR><A  CLASS=\"darklink\" HREF=\"/\">GET THE SOURCE (PHP)</A>\n";
#	print "<BR><A CLASS=\"darklink\" HREF=\"$SCRIPT_FILENAME?do=source\">SOURCE (Perl5/Win32)</A>\n";
	print "<P>This online tic tac toe game is programmed by & (C) F. Permadi";
	if ($FRAMED=="0")
	{
		print "\n</TABLE>";
	}
	print"</CENTER></BODY></HTML>\n";
}