Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

You are to write a lightweight web service in PHP for playing Connect Four games. The Connect Four game is a two-player connection game in

You are to write a lightweight web service in PHP for playing Connect Four games. The Connect Four game is a two-player connection game in which the players take turns dropping their discs from the top into a seven-column, six-row vertically suspended grid [Wikipedia]. The pieces fall straight down, occupying the next available space within the column. The objective of the game is to connect four of one's own discs next to each other vertically, horizontally, or diagonally before your opponent. As a web service, your PHP code provides a few APIs implementing the logic, data or process for playing Connect Four games, not a complete Web application including a GUI. Do not include any GUI components in your web service. The web service APIs are predefined and specified as URLs (see below). Below are key requirements for your web service. R1: The web service shall work with a provided Java client available and downloadble from the course website (c4Client.jar). R2: The web service shall support multiple clients concurrently, each client playing against the server (see R5 below). R3: A game board shall consist of six rows and seven columns, i.e., 6*7 places in which discs can be dropped. R4: A column of the board shall be identified by a 0-based index, e.g., 2 for the third column. R5: The web service shall provide at least two different move strategies for the computer, say Random and Smart. The Smart strategy shall be indeed "smart" to allow for a realistic play. Minimally, it should detect a winning/loosing row, i.e., three consecutive discs with an open end. R6: The web service shall determine the outcome: win, loss, or draw. The web service APIs are implemented as a set of URLs as specified below. All communications between the web service and a client shall be done using the HTTP query string and the JavaScript Object Notation (JSON), a lightweight data-interchange format (see www.json.org) [3]. All inputs to the web service shall be obtained from HTTP query strings, and all outputs to the client shall be written in JSON. Following the REST principles, the web service shall be stateless and provide the following three URLs: 1. http:///info (short for .../info/ or .../info/index.php), where  is the address of your Connect Four service typically consisting of a host name and a pathname. Provide game information, including the board size and available computer move strategies. Below is a sample JSON output. {"width":7,"height":6,"strategies":["Smart","Random"]} Hint: Use json_encode() function to create a JSON (string) representation of a PHP value or object. 2. http:///new?strategy=s Create a new game to play against the specified computer strategy. A normal response will be a JSON string like: {"response":true,"pid":"57cdc4815e1e5"} where pid is a unique play identifier generated by the web service. It will be used to play the newly created game (see 3 below). Upon an error, the web service will notify the client by providing an an appropriate error response like: {"response": false, "reason": "Strategy not specified"} {"response": false, "reason": "Unknown strategy"} Hint: use uniqid() function to generate a unique identifier based on the current time in microseconds. Use the Strategy design pattern to define different strategy classes, e.g., RandomStrategy and SmartStrategy [Chapter 5 of 2]. 3. http:///play?pid=p&move=x Make a move by dropping a disc in the specified column, x, to play the specified game, p. Example: .../play/?pid=57cdc4815e1e5&move=3. A normal response will be a JSON string like: {"response": true, "ack_move": { "slot": 3, "isWin": false, // winning move? "isDraw": false, // draw? "row": []}, // winning row if isWin is true "move": { "slot": 4, "isWin": false, "isDraw": false, "row": []}} where "ack_move" is the acknowledgement and the outcome of the requested move of the player, and "move" is the computer move made right after the player's; there will be no computer move if the player move is a game-ending (win or draw) move. For a winning move, the value of "row" is an array of numbers denoting the indices of the winning row [x1,y1,x2,y2,...,xn,yn], where x's and y's are 0-based column and row indices of places, e.g., "row":[0,5,1,5,2,5,3,5] If there is an error, it should be reported to the client by providing an appropriate error message like: {"response": false, "reason": "Pid not specified"} {"response": false, "reason": "Move not specified"} {"response": false, "reason": "Unknown pid"} {"response": false, "reason": "Invalid slot, 10"} Hint: Define several classes to model the Connect Four game, e.g., Play, Board, etc. The states of some of these classes need to be stored externally, say in a file, because the web service sessions are stateless and the game state should be preserved between sessions. You may use JSON string to persist game states. Hint: You may use the player identifier (pid) as a file name to store the game state, and the game state may be stored as or restored from a JSON string; use json_encode() and json_decode() functions. Hint: Use json_decode() function to convert a JSON encoded string to a PHP value or object. With above three web service URLs, a Connect Four game can be played as follows: Step 1: visit http:///info to find game info Step 2: visit http:///new?strategy=s to create a new game Step 3: repeatedly visit http:///play?pid=p&move=x to drop a disc, e.g. in pseudo code: while (true) { visit http:///play?pid=p&move=x if (ack_move.isWin) { break; // player won } else if (ack_move.isDraw) { break; // draw } else if (move.isWin) { break; // computer won } else if (move.isDraw) { break; // draw } } You can test run a sample implementation at http://www.cs.utep.edu/cheon/cs3360/project/c4/. HINTS It is strongly recommended to use the PHP Development Tools (PDT), an Eclipse plugin for PHP (or PhpStorm for IntellJ IDEA). Create in your src directory/folder three subdirectories/folders corresponding to the three URLs. Each subdirectory should have a PHP file named index.php, the entry point of the provided API, e.g., info/index.php new/index.php play/index.php If this is your first PHP project, start early. You need a bit of time to get familiar with PHP scripting and to configure and get used to its support tools and development environment.

A simple API to play the Connect Four game. The API consists of three URLs: info, new and play.

Info

Provide information about the service, including the dimension of the grid and the computer move strategies.

Request URL http://www.cs.utep.edu/cheon/cs3360/project/c4/info/

Response Service information encoded in JSON

JSON fields in response body:

Field Type Description
width Integer Number of columns of the grid
height Integer Number of rows of the grid
strategies Array Supported computer move strategies

Example: see Java client code.

Java client code:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class JavaClient { /** * Retrieve the document at the specified URL by sending a GET request; * return null if the request/connection fails. */ public String sendGet(String urlString) { HttpURLConnection con = null; try { URL url = new URL(urlString); con = (HttpURLConnection) url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = in.readLine()) != null) { response.append(line); } return response.toString(); } catch (IOException e) { //e.printStackTrace(); } finally { if (con != null) { con.disconnect(); } } return null; } public static void main(String[] args) { String url = "http://www.cs.utep.edu/cheon/cs3360/project/c4/info/"; String response = new JavaClient().sendGet(url); System.out.println(response); } }
{"width": 7, "height": 6, "strategies": ["Smart","Random"]} 

New

Create a new game to play against the specified computer strategy.

Request URL http://www.cs.utep.edu/cheon/cs3360/project/c4/new/?strategy=s

Required request parameters:

Name Type Description
strategy String Computer move strategy, e.g., Smart

Response Unique Id of the newly created game, or an error message.

JSON fields in response body:

Field Type Description
response Boolean True if the request is accepted; false otherwise
pid String Unique ID of the newly created game
reason String Error message

Examples: see Java client code.

Java client code:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class JavaClient { /** * Retrieve the document at the specified URL by sending a GET request; * return null if the request/connection fails. */ public String sendGet(String urlString) { HttpURLConnection con = null; try { URL url = new URL(urlString); con = (HttpURLConnection) url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = in.readLine()) != null) { response.append(line); } return response.toString(); } catch (IOException e) { //e.printStackTrace(); } finally { if (con != null) { con.disconnect(); } } return null; } public static void main(String[] args) { String FORMAT = "http://www.cs.utep.edu/cheon/cs3360/project/c4/new/?strategy=%s"; String strategy = "Smart"; String url = String.format(FORMAT, strategy); String response = new JavaClient().sendGet(url); System.out.println(response); } }
{"response": true, "pid": "57cdc4815e1e5"} {"response": false, "reason": "Strategy not specified"} {"response": false, "reason": "Unknown strategy"} 

Play

Make a move by dropping a disc in a slot.

Request URL http://www.cs.utep.edu/cheon/cs3360/project/c4/play/?pid=p&move=x

Required request parameters:

Name Type Description
pid String Id of the game (provided by the new API)
move Integer 0-based column (slot) index

Response Acknowledgement of the player's move along with the computer's move, or an error message.

JSON fields in response body:

Field Type Description
response Boolean True if the request is accepted; false otherwise
ack_move Object Acknowledgement and the outcome of the requested move of the player
move Object Computer's move if the player's move is not a win or draw move

Both the ack_move and move have the following fields:

Field Type Description
slot Integer 0-based index of the slot in which a disc is dropped
isWin Boolean True if this move is a winning move; false otherwise
isDraw Boolean True if this move is a draw move (fills the last place); false otherwise
win Array Indices of the winning row [x1,y1,x2,y2,...,xn,yn] if this move is a winning move, where x's and y's are 0-based column and row indices of places, e.g., [0,5,1,5,2,5,3,5]

Examples: see Java client code.

Java client code:

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; public class JavaClient { /** * Retrieve the document at the specified URL by sending a GET request; * return null if the request/connection fails. */ public String sendGet(String urlString) { HttpURLConnection con = null; try { URL url = new URL(urlString); con = (HttpURLConnection) url.openConnection(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); StringBuilder response = new StringBuilder(); String line; while ((line = in.readLine()) != null) { response.append(line); } return response.toString(); } catch (IOException e) { //e.printStackTrace(); } finally { if (con != null) { con.disconnect(); } } return null; } public static void main(String[] args) { String FORMAT = "http://www.cs.utep.edu/cheon/cs3360/project/c4/play/?pid=%s&move=%d"; String pid = "57cdc4815e1e5"; int slot = 3; String url = String.format(FORMAT, pid, slot); String response = new JavaClient().sendGet(url); System.out.println(response); } }
{"response": true, "ack_move": { "slot": 3, "isWin": false, // winning move? "isDraw": false, // draw? "row": []}, // winning row if isWin is true "move": { "slot": 4, "isWin": false, "isDraw": false, "row": []}} {"response": false, "reason": "Pid not specified"} {"response": false, "reason": "Move not specified"} {"response": false, "reason": "Unknown pid"} {"response": false, "reason": "Invalid slot, 10"} 

Test with this code:

/** Test: play response */ function testPlay8() { $TAG = "P8"; $response = visitPlay(createGame(), "3"); $json = json_decode($response); $ackMove = property($json, 'ack_move'); assertTrue(isSet($ackMove), "$TAG-1"); $slot = property($ackMove, 'slot'); assertTrue(isSet($slot) && $slot == 3, "$TAG-2"); $isWin = property($ackMove, 'isWin'); assertTrue(isSet($isWin) && !$isWin, "$TAG-3"); $isDraw = property($ackMove, 'isDraw'); assertTrue(isSet($isDraw) && !$isDraw, "$TAG-4"); $row = property($ackMove, 'row'); assertTrue(isSet($row) && is_array($row) && empty($row), "$TAG-5"); } /** Test: play response */ function testPlay9() { global $numOfSlots; $TAG = "P9"; $response = visitPlay(createGame(), "3"); $json = json_decode($response); $move = property($json, 'move'); assertTrue(isSet($move), "$TAG-1"); $slot = property($move, 'slot'); assertTrue(isSet($slot) && $slot >= 0 && $slot < $numOfSlots, "$TAG-2"); $isWin = property($move, 'isWin'); assertTrue(isSet($isWin) && !$isWin, "$TAG-3"); $isDraw = property($move, 'isDraw'); assertTrue(isSet($isDraw) && !$isDraw, "$TAG-4"); $row = property($move, 'row'); assertTrue(isSet($row) && is_array($row) && empty($row), "$TAG-5"); } /** Test: partial game - place several discs. */ function testPlay10() { global $numOfSlots; $TAG = "P10"; $pid = createGame(); for ($i = 0; $i < 3; $i++) { // pick an arbitray slot $slot = rand(0, $numOfSlots - 1); $response = visitPlay($pid, $slot); checkPlayResponse($response, true, $TAG); } } /** Test: concurrent games. */ function testPlay11() { $TAG = "P11"; $g1 = createGame(); play($g1, "1", true, "$TAG-1"); $g2 = createGame(); play($g2, "1", true, "$TAG-2"); assertTrue($g1 != $g2, "$TAG-3"); // differed play Ids. } //- helper methods function visitNew($strategy = null) { global $home; $query = ''; if (!is_null($strategy)) { $query = '?strategy=' . $strategy; } return @file_get_contents($home . "/new/index.php" . $query); } function checkNewResponse($response, $expected, $msg) { if ($response) { $json = json_decode($response); if ($json != null) { $r = property($json, 'response'); assertTrue(isSet($r) && $r == $expected, $msg); if ($expected) { $pid = property($json, 'pid'); assertTrue(isSet($pid), $msg); } return; } } fail($msg); } function createGame() { global $strategies; $strategy = "Random"; if (count($strategies) > 0) { $strategy = $strategies[0]; } $response = visitNew($strategy); $json = json_decode($response); return property($json, 'pid'); } function play($pid = null, $move = null, $ok, $tag) { $response = visitPlay($pid, $move); checkPlayResponse($response, $ok, $tag); } function visitPlay($pid = null, $move = null) { global $home; $query = ''; if (!is_null($pid)) { $query = '?pid=' . $pid; } if (!is_null($move)) { $query = $query . (strlen($query) > 0 ? '&' : '?'); $query = $query . 'move=' . $move; } return @file_get_contents($home . "/play/index.php" . $query); } function checkPlayResponse($response, $expected, $msg) { if ($response) { $json = json_decode($response); if ($json != null) { $r = property($json, 'response'); assertTrue(isSet($r) && $r == $expected, $msg); if ($expected) { $ack = property($json, 'ack_move'); assertTrue(isSet($ack), $msg); } return; } } fail($msg); } //--------------------------------------------------------------------- // Simple testing framework //--------------------------------------------------------------------- /** Run all user-defined functions named 'test'. */ function runTests() { $count = 0; $prefix = "test"; $functions = get_defined_functions (); $names = $functions ['user']; foreach ($names as $name) { if (substr($name, 0, strlen($prefix)) === $prefix) { $count ++; echo "."; call_user_func($name); } } summary($count, fail('', false)); } function assertTrue($expr, $msg) { if (!$expr) { fail($msg); } } function fail($msg, $report = true) { static $count = 0; static $tested = array(); if ($report) { $prefix = explode('-', $msg); $testId = $prefix[0]; // e.g., P1 from P1-1 if (!in_array($testId, $tested)) { $tested[] = $testId; $count++; echo "F($msg)"; } } return $count; } function summary($total, $failed) { echo " "; echo "Failed/Total: $failed/$total "; } ?>

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Data Management Databases And Organizations

Authors: Richard T. Watson

6th Edition

1943153035, 978-1943153039

More Books

Students also viewed these Databases questions

Question

What is the most important part of any HCM Project Map and why?

Answered: 1 week ago