function getGameInfo($gameID, $lastUpdated = 0, $ignoreUpdateTime = false) { global $sqlhost, $sqlusername, $sqlpassword; if ($ignoreUpdateTime) { $lastUpdated = 0; } $conn = new mysqli($sqlhost, $sqlusername, $sqlpassword); if ($conn->connect_error) { error_log("getGameInfo.php - Connection failed: " . $conn->connect_error); return ""; } if ($timeStmt = $conn->prepare("SELECT UNIX_TIMESTAMP(lastUpdated), fullUpdate FROM sweepelite.games WHERE gameID = ?")) { $updatedTime = null; $timeStmt->bind_param("i", $gameID); $timeStmt->execute(); $timeStmt->bind_result($updated, $fullUpdate); while ($timeStmt->fetch()) { $updatedTime = $updated; } $timeStmt->close(); if ($updatedTime !== null) { if ($updatedTime > $lastUpdated) { $ret = new SimpleXMLElement("<update/>"); $ret->addAttribute("id", $gameID); if ($fullUpdate || $ignoreUpdateTime) { #Select all information about the game from the game's status columns in the MySQL database and parse it into XML form. if ($query = $conn->prepare("SELECT map, visibility, friendlyTanks, enemyTanks, wrecks, traps, height, width, status FROM sweepelite.games WHERE gameID = ?")) { $query->bind_param("i", $gameID); $query->execute(); $query->bind_result($map, $vis, $friendlyTanks, $enemyTanks, $wrecks, $traps, $height, $width, $status); $query->fetch(); $query->close(); $finalFriendlies = translateTanksToPHP($friendlyTanks); $finalEnemies = translateTanksToPHP($enemyTanks); $finalWrecks = translateTanksToPHP($wrecks); $finalTraps = translateTrapsToPHP($traps); $finalMap = translateMinefieldToMySQL(getMinefieldWithVisibility($gameID, translateMinefieldToPHP($map, $height, $width), translateMinefieldToPHP($vis, $height, $width), $finalWrecks)); $ret->addChild('map', $finalMap); $ret->addChild('height', $height); $ret->addChild('width', $width); $ret->addChild('status', $status); if ($finalFriendlies !== null) { $ret->addChild('friendlyTanks'); foreach ($finalFriendlies as $k => $v) { if (count($v) === 2) { $ret->friendlyTanks->addChild('tank', $v[0] . "," . $v[1]); } } } if ($finalEnemies !== null) { $ret->addChild('enemyTanks'); foreach ($finalEnemies as $k => $v) { if (count($v) === 2) { $ret->enemyTanks->addChild('tank', $v[0] . "," . $v[1]); } } } if ($finalTraps !== null) { $ret->addChild('traps'); foreach ($finalTraps as $k => $v) { if (count($v) === 3) { $ret->traps->addChild('trap', $v[0] . "," . $v[1] . "," . $v[2]); } } } if ($hsStmt = $conn->prepare("SELECT MAX(totalScore) FROM sweepelite.players")) { $hsStmt->execute(); $hsStmt->bind_result($hs); $hsStmt->fetch(); $hsStmt->close(); #Add all players in the game and their statuses to the XML. if ($playerQuery = $conn->prepare("SELECT p.username, p.playerID, s.status, s.trapType, s.trapCooldown, s.digNumber, s.flagNumber, p.totalScore FROM sweepelite.players as p INNER JOIN sweepelite.playerstatus as s ON p.playerID=s.playerID WHERE s.gameID=?")) { $playerQuery->bind_param("i", $gameID); $playerQuery->execute(); $playerQuery->bind_result($user, $currentID, $status, $trapType, $trapCooldown, $digNumber, $flagNumber, $totalScore); $ret->addChild('players'); while ($playerQuery->fetch()) { $playerInfo = $ret->players->addChild('player', $user); $playerInfo->addAttribute('status', $status); $playerInfo->addAttribute('trapType', $trapType); $playerInfo->addAttribute('trapCooldown', $trapCooldown); $medals = calculateMedalAttributesForPlayer($digNumber, $flagNumber); $playerInfo->addAttribute('digMedal', $medals['digMedal']); $playerInfo->addAttribute('flagMedal', $medals['flagMedal']); $scoreRank = 5; //Calculate score rank if ($totalScore >= $hs) { $scoreRank = 6; } else { if ($totalScore === 0) { $scoreRank = 0; } else { if ($totalScore < $hs * 0.2) { $scoreRank = 1; } else { if ($totalScore < $hs * 0.4) { $scoreRank = 2; } else { if ($totalScore < $hs * 0.6) { $scoreRank = 3; } else { if ($totalScore < $hs * 0.8) { $scoreRank = 4; } } } } } } $playerInfo->addAttribute('scoreRank', $scoreRank); } $playerQuery->close(); if ($gameTimeStmt = $conn->prepare("SELECT v FROM sweepelite.globalvars WHERE k='nextGameTime'")) { $gameTimeStmt->execute(); $gameTimeStmt->bind_result($time); while ($gameTimeStmt->fetch()) { $ret->addChild('nextGameTime', $time); } } else { error_log("getGameInfo.php - Unable to prepare next game time statement. " . $conn->errno . ": " . $conn->error); } } else { error_log("getGameInfo.php - Unable to prepare player gathering statement. " . $conn->errno . ": " . $conn->error); $ret->addChild('error', "Internal error occurred, please try again later."); } } else { error_log("getGameInfo.php - Unable to prepare high score gathering statement. " . $conn->errno . ": " . $conn->error); $ret->addChild('error', "Internal error occurred, please try again later."); } if (!$ignoreUpdateTime) { if ($updateQuery = $conn->prepare("UPDATE sweepelite.games SET fullUpdate=0 WHERE gameID=?")) { $updateQuery->bind_param("i", $gameID); $updateQuery->execute(); $updateQuery->close(); } } } } #Add other player actions $otherPlayers = getPlayerActionsForGame($gameID); if (count($otherPlayers) !== 0) { $opNode = $ret->addChild('otherPlayers'); foreach ($otherPlayers as $k => $v) { if (count($v) === 2) { $opNode->addChild('otherPlayer', $v[0] . "," . $v[1]); } } } $finalRet = str_replace('<?xml version="1.0"?>', "", $ret->asXML()); return $finalRet; } } } return ""; }
function resolveAllActions($gameID) { global $sqlhost, $sqlusername, $sqlpassword, $adjacencies; #Initialize the connection to the MySQL database. $conn = new mysqli($sqlhost, $sqlusername, $sqlpassword); if ($conn->connect_error) { die("resolveActions.php - Connection failed: " . $conn->connect_error); } #Get map information, both the minefield and the visibility, for this game. if ($stmt = $conn->prepare("SELECT map, visibility, height, width, friendlyTankCountdown, friendlyTanks, enemyTankCountdown, enemyTankCountdownReset, enemyTanks, wrecks, traps FROM sweepelite.games WHERE gameID=?")) { $stmt->bind_param("i", $gameID); $stmt->execute(); $stmt->bind_result($m, $v, $h, $w, $tankCount, $t, $enemyTankCount, $enemyTankReset, $e, $wr, $tr); if ($stmt->fetch()) { $stmt->close(); #Determine if this is the first ever move in the game. If so, different actions call for different outcomes. $firstMove = false; if (strpos($v, "1") === false && strpos($v, "2") === false) { $firstMove = true; } $minefield = translateMinefieldToPHP($m, $h, $w); $visibility = translateMinefieldToPHP($v, $h, $w); $friendlyTanks = translateTanksToPHP($t); $enemyTanks = translateTanksToPHP($e); $wrecks = translateTanksToPHP($wr); $traps = translateTrapsToPHP($tr); #Determine if we need to expand the map first due to tank movement. $expand = false; $baseExploded = false; foreach ($friendlyTanks as $tankKey => $tankPos) { if ($tankPos[0] == $w - 1) { $expand = true; } } foreach ($enemyTanks as $tankKey => $tankPos) { if ($tankPos[0] == 0) { $baseExploded = true; } } if ($expand) { $newVals = expandMinefield($minefield, $visibility, 10, 0); $minefield = $newVals["minefield"]; $visibility = $newVals["visibility"]; if ($enemyTankReset > 3) { $enemyTankReset = $enemyTankReset - 1; } } #Get all player information $allPlayers = getPlayersForGame($gameID); if (count($allPlayers) > 0) { #Retrieve all actions in the action queue for this game and throw them into unique objects in an array for easy access during resolution. if ($actionStmt = $conn->prepare("SELECT playerID, actionType, xCoord, yCoord FROM sweepelite.actionqueue WHERE gameID=?")) { $actionqueue = array(); $actionStmt->bind_param("i", $gameID); $actionStmt->execute(); $actionStmt->bind_result($playerID, $actionType, $xCoord, $yCoord); while ($actionStmt->fetch()) { $temp = array("playerID" => intval($playerID), "actionType" => intval($actionType), "x" => intval($xCoord), "y" => intval($yCoord)); array_push($actionqueue, $temp); } $actionStmt->close(); $queuedTraps = array(); while (count($actionqueue) > 0) { $cur = array_shift($actionqueue); #Check that current action is legal according to tank placement. $legalMove = true; foreach ($friendlyTanks as $tankKey => $tankPos) { if ($cur['x'] == $tankPos[0]) { if ($cur['y'] == $tankPos[1]) { $legalMove = false; } } } foreach ($enemyTanks as $tankKey => $tankPos) { if ($cur['x'] == $tankPos[0]) { if ($cur['y'] == $tankPos[1]) { $legalMove = false; } } } foreach ($wrecks as $wkey => $wval) { if ($cur['x'] == $wval[0]) { if ($cur['y'] == $wval[1]) { $legalMove = false; } } } foreach ($traps as $wkey => $tval) { if ($cur['x'] == $tval[1]) { if ($cur['y'] == $tval[2]) { $legalMove = false; } } } if ($legalMove) { #Action Type 0 is a shovel action. #When shoveling, the current tile is revealed no matter what. #If the tile is a mine, this kills the current player. #If the tile had a value of 0, new actions are added to the queue for revealing all adjacent tiles to the shoveled tile, barring any that are already not flagged or visible. if ($cur["actionType"] === 0) { #Check if we have previously shoveled this tile or not. $notShoveled = true; foreach (getPlayerValue($allPlayers, $cur["playerID"], "dugTiles") as $key => $shoveled) { if ($cur['x'] == $shoveled[0]) { if ($cur['y'] == $shoveled[1]) { $notShoveled = false; } } } if ($notShoveled) { $visibility[$cur["x"]][$cur["y"]] = 2; if ($minefield[$cur["x"]][$cur["y"]] === "M") { if ($firstMove) { #Temporarily just remove the mine. $minefield[$cur["x"]][$cur["y"]] = 0; $visibility[$cur["x"]][$cur["y"]] = 0; $minefield = updateMinefieldNumbers($minefield); $newAction = array("playerID" => $cur["playerID"], "actionType" => $cur["actionType"], "x" => $cur["x"], "y" => $cur["y"]); array_push($actionqueue, $newAction); } else { $tempDugTiles = getPlayerValue($allPlayers, $cur["playerID"], "dugTiles"); array_push($tempDugTiles, array($cur["x"], $cur["y"])); $allPlayers = setPlayerValue($allPlayers, $cur["playerID"], "dugTiles", $tempDugTiles); $allPlayers = setPlayerValue($allPlayers, $cur["playerID"], 'status', 0); } } else { if ($minefield[$cur["x"]][$cur["y"]] == 0) { $tempDugTiles = getPlayerValue($allPlayers, $cur["playerID"], "dugTiles"); array_push($tempDugTiles, array($cur["x"], $cur["y"])); $allPlayers = setPlayerValue($allPlayers, $cur["playerID"], "dugTiles", $tempDugTiles); foreach ($adjacencies as $adj) { $targetX = $cur["x"] + $adj[0]; $targetY = $cur["y"] + $adj[1]; $shouldAdd = true; if ($targetX < 0 or $targetX >= $w) { $shouldAdd = false; } if ($targetY < 0 or $targetY >= $h) { $shouldAdd = false; } if ($shouldAdd) { $noTanks = true; foreach ($friendlyTanks as $tankKey => $tankPos) { if ($targetX == $tankPos[0]) { if ($targetY == $tankPos[1]) { $noTanks = false; } } } foreach ($enemyTanks as $tankKey => $tankPos) { if ($targetX == $tankPos[0]) { if ($targetY == $tankPos[1]) { $noTanks = false; } } } foreach ($wrecks as $wkey => $wval) { if ($targetX == $wval[0]) { if ($targetY == $wval[1]) { $noTanks = false; } } } if ($noTanks) { if ($visibility[$targetX][$targetY] == 0) { $newAction = array("playerID" => $cur["playerID"], "actionType" => $cur["actionType"], "x" => $targetX, "y" => $targetY); array_push($actionqueue, $newAction); } } } } } else { $tempDugTiles = getPlayerValue($allPlayers, $cur["playerID"], "dugTiles"); array_push($tempDugTiles, array($cur["x"], $cur["y"])); $allPlayers = setPlayerValue($allPlayers, $cur["playerID"], "dugTiles", $tempDugTiles); } } } #Action Type 1 is a flag action. #If the tile is unrevealed, a flag is placed there instead. } else { if ($cur["actionType"] === 1) { if ($visibility[$cur["x"]][$cur["y"]] == 0) { $visibility[$cur["x"]][$cur["y"]] = 1; if ($minefield[$cur["x"]][$cur["y"]] === "M") { $allPlayers = alterPlayerValue($allPlayers, $cur["playerID"], "correctFlags", 1); } $allPlayers = alterPlayerValue($allPlayers, $cur["playerID"], "flagNumber", 1); } #Action Type 2 is a place trap action. #Place a trap on the space. } else { if ($cur["actionType"] === 2) { $cooldown = getPlayerValue($allPlayers, $cur["playerID"], "trapCooldown"); if ($cooldown <= 0) { $trapVal = getPlayerValue($allPlayers, $cur["playerID"], "trapType"); $tempTrap = array($trapVal, $cur["x"], $cur["y"]); array_push($queuedTraps, $tempTrap); $allPlayers = setPlayerValue($allPlayers, $cur["playerID"], "trapCooldown", getCooldownForTrapType($trapVal)); } } } } } else { error_log("resolveActions.php - Illegal move made by player ID " . $cur["playerID"] . " at coordinates " . $cur["x"] . "," . $cur["y"]); } if (getPlayerValue($allPlayers, $cur['playerID'], 'hasActed') === 0) { if ($cur['actionType'] === 0) { $oldCooldown = getPlayerValue($allPlayers, $cur['playerID'], 'trapCooldown'); if ($oldCooldown > 0) { $allPlayers = setPlayerValue($allPlayers, $cur['playerID'], 'trapCooldown', $oldCooldown - 1); } } } $allPlayers = setPlayerValue($allPlayers, $cur['playerID'], 'hasActed', 1); } #Resolve traps $trapResults = resolveTraps($minefield, $visibility, $friendlyTanks, $enemyTanks, $traps, $wrecks); $traps = $trapResults['traps']; #Add queued traps foreach ($queuedTraps as $trapKey => $trapSpecifics) { $traps = addTrap($traps, $trapSpecifics[0], $trapSpecifics[1], $trapSpecifics[2]); } #All tanks are updated, and any stuff on the map is updated to reflect these changes. $updatedTanks = updateTanks($trapResults['map'], $trapResults['visibility'], $trapResults['friendlyTanks'], $trapResults['enemyTanks'], $trapResults['wrecks']); $friendlyTanks = $updatedTanks['updatedFriendlyTanks']; $enemyTanks = $updatedTanks['updatedEnemyTanks']; $wrecks = $updatedTanks['updatedWrecks']; $visibility = $updatedTanks['updatedVisibility']; #Update the tank count. If it is at 0, add a tank and reset the count to 3. $tankCount = $tankCount - 1; if ($tankCount <= 0) { $addedTank = addFriendlyTank($minefield, $visibility); if ($addedTank['newTankPosition'] !== null) { array_push($friendlyTanks, $addedTank['newTankPosition']); } if ($addedTank['newVisibility'] !== null) { $visibility = $addedTank['newVisibility']; } $tankCount = 3; } #Update the enemy tank count. If it is at 0, add an enemy and reset the count to the current threshold. $enemyTankCount = $enemyTankCount - 1; if ($enemyTankCount <= 0) { $addedTank = addEnemyTank($minefield, $visibility); if ($addedTank['newTankPosition'] !== null) { array_push($enemyTanks, $addedTank['newTankPosition']); } if ($addedTank['newVisibility'] !== null) { $visibility = $addedTank['newVisibility']; } $enemyTankCount = $enemyTankReset; } #All players who did submit actions are updated appropriately. $allPlayers = savePlayersForGame($allPlayers, $gameID); #Empty the action queue of actions relating to this specific game. if ($deleteStmt = $conn->prepare("DELETE FROM sweepelite.actionqueue WHERE gameID=?")) { $deleteStmt->bind_param("i", $gameID); $updated = $deleteStmt->execute(); if ($updated === false) { error_log("resolveActions.php - Error occurred during action queue clean up. " . $deleteStmt->errno . ": " . $deleteStmt->error); $deleteStmt->close(); } else { $deleteStmt->close(); #Try to determine if the game is complete or not. $gameCompleted = false; if ($baseExploded) { $gameCompleted = true; error_log("Game Over - Base blown up!"); } else { if (countPlayersWithValue($allPlayers, 'status', 1) > 0) { $gameCompleted = true; for ($x = 0; $x < count($visibility) && $gameCompleted; $x++) { for ($y = 0; $y < count($visibility[$x]) && $gameCompleted; $y++) { if ($visibility[$x][$y] == 0) { if ($minefield[$x][$y] === "M") { $gameCompleted = false; } } } } if ($gameCompleted) { error_log("Game Over - No unrevealed non-mine tiles left!"); } } else { $gameCompleted = true; error_log("Game Over - No living players left!"); } } #If game is done, all unrevealed tiles become visible instead. if ($gameCompleted) { for ($x = 0; $x < count($visibility); $x++) { for ($y = 0; $y < count($visibility[$x]); $y++) { if ($minefield[$x][$y] === "M") { $visibility[$x][$y] = 2; } } } $allPlayers = setAllPlayersValue($allPlayers, "status", 0); savePlayerScores($allPlayers); } #Update map and visibility values for the game by saving to database. if ($updateStmt = $conn->prepare("UPDATE sweepelite.games SET map=?, visibility=?, friendlyTankCountdown=?, friendlyTanks=?, enemyTankCountdown=?, enemyTanks=?, enemyTankCountdownReset=?, wrecks=?, traps=?, status=?, width=?, height=?, lastUpdated=NOW(), fullUpdate=1 WHERE gameID=?")) { $statusStr = "OPEN"; if ($gameCompleted) { $statusStr = "GAME OVER"; } $updateStmt->bind_param("ssisisisssiii", translateMinefieldToMySQL($minefield), translateMinefieldToMySQL($visibility), $tankCount, translateTanksToMySQL($friendlyTanks), $enemyTankCount, translateTanksToMySQL($enemyTanks), $enemyTankReset, translateTanksToMySQL($wrecks), translateTrapsToMySQL($traps), $statusStr, count($minefield), count($minefield[0]), $gameID); $updated = $updateStmt->execute(); if ($updated === false) { error_log("resolveActions.php - Error occurred during map update. " . $updateStmt->errno . ": " . $updateStmt->error); } $updateStmt->close(); if ($gameCompleted) { return false; } } else { error_log("resolveActions.php - Unable to prepare map update after resolving action queue. " . $conn->errno . ": " . $conn->error); } } } else { error_log("resolveActions.php - Unable to prepare delete statement. " . $conn->errno . ": " . $conn->error); } } else { error_log("resolveActions.php - Unable to prepare action statement for resolving action queue. " . $conn->errno . ": " . $conn->error); } } else { error_log("resolveActions.php - Did not find any players for current game."); } } else { error_log("resolveActions.php - Unable to resolve map statement for resolving action queue. " . $stmt->errno . ": " . $stmt->error); } } else { error_log("resolveActions.php - Unable to prepare map statement for resolving action queue. " . $conn->errno . ": " . $conn->error); } return true; }