My first ever question on here as I'm completely stuck, so apologies if I leave out any key information - please let me know!
I am creating a PHP Battleships game and trying to use full OO. I'm really close, however, an array for one of my classes does not hold any updates I make to it.
First off, I dynamically
created a HTML table with an onclick event - which passes the coordinates to a JS function.
I then make an AJAX call in jQuery:
function shotFired(row, column) {
var coords = {
x: row,
y: column
};
$.post("data/game_controller.php", {
jsonCoords: JSON.stringify(coords)
}, function(results) {
console.log(results)
console.log(results[4])
var playerShotResult = results[0];
var computerShotX = results[1] + 1;
var computerShotY = results[2] + 1;
var computerShotResult = results[3];
var positionsClicked = document.getElementById("computer_" + row + "," + column)
switch (playerShotResult) {
case "MISS":
positionsClicked.classList.add("miss");
break;
case "HIT":
positionsClicked.classList.add("hit");
break;
case "Already Hit":
document.getElementById("outputMessage").innerHTML = result
break;
default:
console.log("Player shot defaulted");
}
}, "json")
I then use game_controller.php to handle the request and call shotFired:
<?php
session_start();
require("../classes/Game.class.php");
if (isset($_POST['jsonCoords'])) {
if (isset($_SESSION['newGame'])) {
$game = unserialize($_SESSION['newGame']);
$coords = json_decode($_POST['jsonCoords']);
$results = $game->shotFired($coords->x, $coords->y);
echo json_encode($results);
}
}
shotFired from the Game.php Class file, gets an instance of the Fleet class called computer, and runs the checkPosition function:
public function shotFired($x, $y)
{
$computer = $this->getComputer();
$playerHit = $computer->checkPosition(($x - 1), ($y - 1));
$computerGrid = $computer->getBattleshipsGrid();
$computerHit = $this->simulateComputerShot();
return [$playerHit, $computerHit[0], $computerHit[1], $computerHit[2], $computerGrid];
}
checksPosition checks the State of the Position instance in the BattleshipGrid array, and then attempts to update the array with a H or M - using a standard setter method:
public function checkPosition($x, $y): string
{
$positionObj = $this->battleshipsGrid["(" . $x . "," . $y . ")"];
$positionState = $positionObj->getState();
if ($positionState == "#") {
$positionObj->setState("M");
return "MISS";
} elseif ($positionState == "M" || $positionState == "H") {
return "Already Fired";
} else {
$positionObj->setState("H");
return "HIT";
}
}
For reference, I set the Battleships board in the constructor for Fleet.php:
// Populate associative array with instances of position
for ($y = 0; $y < $gridSize; $y++) {
for ($x = 0; $x < $gridSize; $x++) {
$coordinates = "(" . $x . "," . $y . ")";
$this->battleshipsGrid[$coordinates] = new Position($x, $y);
}
}
It works directly after it has been set - however, on the next onclick event, the H or M value is reset to it's previous value?
Seen here in console output
After a couple of hours, the closest I've come to is passing byRef in the setState function (didn't make a difference).
I've seen some notes on array_map, but I'm not sure this is what I'm looking for?
For ref, this is how I output the battleshipGrid to the console:
public function getBattleshipsGrid()
{
$readableGrid = "";
$grid = $this->battleshipsGrid;
foreach ($grid as $coordsID => $positionObj) {
$readableGrid .= "\n" . $coordsID . ": " . $positionObj->getState();
}
return $readableGrid;
}
Apologies for the long post, but I didn't want to leave anything out. Any and all help would be extremely appreciated!
Many thanks
It looks like you're not saving the state of the coordinates of the hits. If you are using the eloquent model, and setState is changing the attribute's value, make sure that you call $positionObj->save() as php does not save state on each ajax request. You will need to use a database or some sort of storage to have the server 'remember' that you clicked a specific location.
Related
just developed a simple CLI php code that calculates the multiplication table of your choosen size but when I check the files to make sure they stick to PSR coding standards, it gives me four errors/violations. I don't know where in the files the errors are after several attempts and days of work on the files.
there are two files:
cliVersion.php and generateCLITable.php
The first file gives me 1 PSR error and the second one gives me 3 PSR errors.
this is how I generate the multiplication table of size 12 on command line :
php cliVersion.php 12
can anyone help me to find out the PSR errors in the files.
here's the files and the error report:
cliVersion.php
<?php
declare(strict_types=1);
require_once 'generateCLITable.php';
require_once '../model/validateInput.php';
?>
<?php
// Assign the user's input argument value to $input variable
$inputString = $argv[1];
$errorMessage = "Please enter a valid argument (a whole number greater than 1)";
// Check if the user's input argument is not null or empty
if ($inputString == null || $inputString == "") {
echo $errorMessage;
} else {
// Create an object of ValidateInput Class
$inputData = new ValidateInput();
/*
Validate the $input variable received from the user as an argument.
The code will be safe to be processed after this line.
*/
$validatedInput = $inputData->validateInputData($inputString);
$validatedInputInt = (int)$validatedInput;
/*
Check if the validated input is an Integer and if it is,
generates the table else returns the error message
*/
$isInputValidInt = $inputData->isInputInt($validatedInputInt);
if ($isInputValidInt && $validatedInputInt > 1) {
$multTable = new MultTable();
$multTable->generateTable($validatedInputInt);
} else {
echo $errorMessage;
}
}
echo PHP_EOL;
GenerateCLITable.php
<?php
declare(strict_types=1);
class MultTable
{
/**
* The public generateTable function generates the multiplication table
*
* #param int $inputValue
*
* #return void
*/
public function generateTable(int $inputValue)
{
// Create first row of table headers - green colour
for ($col=1; $col <= $inputValue; $col++) {
echo "\033[35m \t$col \033[0m";
}
// Create remaining rows
for ($row=1, $col=1; $row <= $inputValue; $row++) {
echo "\n";
// First cell is a table header - green colour
if ($col == 1) {
echo "\033[35m \n$row \033[0m";
}
while ($col <= $inputValue) {
echo "\t" . $row * $col++ ;
}
// Reset $col at the end of the row
$col = 1;
}
}
}
Error report:
cliVersion.php
----------------------------------------------------------------------
FOUND 1 ERROR AFFECTING 1 LINE
generateCLITable.php
----------------------------------------------------------------------
FOUND 3 ERRORS AFFECTING 3 LINES
I strongly recommend to use https://github.com/FriendsOfPHP/PHP-CS-Fixer in such cases.
I guess you are trying to unit test the code. I would probably focus on trying to be as thoughtful with the code first . Take cliVersion, I think you can simplify it greatly by creating a single method to handle the input. Also I find a lot of issue with your comments and variables and overdesign.
Revised Version:
<?php
//declare(strict_types=1); // I'm using php 5 so I don't have this (but ok for you)
require_once 'generateCLITable.php';
require_once '../model/validateInput.php';
$must_be_gtr_than = 1; // set floor val for validation
$inputData = new ValidateInput($must_be_gtr_than);
$multTable = new MultTable();
if ($inputData->isValidIntegerData($argv[1])) {
$tableSize = (int) $argv[1];
$multTable->generateTable($tableSize);
} else {
echo "Error: Please enter a valid argument (a whole number greater than ".$must_be_gtr_than.")";
die();
}
In this example I have one method alone that validates that I have an integer > 0, if isValidIntegerData than proceed . Think about ease of code and names that make sense generateTable doesn't tell me much, is there a better name ? Things like that. Take "$validatedInputInt", that doesn't speak to what you are doing as well as what that int is, "tableSize" makes more sense to me. Also we many times save camel back for Classes etc.. I would look up PSR2.
For example we use
CONST_SOMETHING = xx; //constants
var_x or varx // lowercase for vars
methodX //camel back
ClassName // ucwords for classes
etc..
UPDATE:
This is how I would probably go about building something like this:
<?php
//declare(strict_types=1); // PHP5.x Example
class GenerateCLITable
{
const DEFAULT_BOARD_SIZE = 12;
public $game_board, $table_size;
public function __construct($input = self::DEFAULT_BOARD_SIZE)
{
$this->game_board = $this->generateTable($input);
}
public function generateTable($inputValue = self::DEFAULT_BOARD_SIZE)
{
$table = "";
// Create first row of table headers - green colour
for ($col=1; $col <= $inputValue; $col++) {
$table .= "\033[35m \t$col \033[0m";
}
// Create remaining rows
for ($row=1, $col=1; $row <= $inputValue; $row++) {
$table .= "\n";
// First cell is a table header - green colour
if ($col == 1) {
$table .= "\033[35m \n$row \033[0m";
}
while ($col <= $inputValue) {
$table .= "\t" . $row * $col++ ;
}
// Reset $col at the end of the row
$col = 1;
}
$this->game_board = $table;
$this->table_size = $inputValue;
return $table;
}
public function isValidInputValue($input = '', $size = 0)
{
return (!empty($input) && $input > $size) ? (is_int((int) $input)) : false;
}
}
//require_once 'generateCLITable.php';
$multTable = new GenerateCLITable();
$must_be_gtr_than = 1;
if (!$multTable->isValidInputValue($argv[1], $must_be_gtr_than)) {
echo "Error: Please enter a valid argument (a whole number greater than ".$must_be_gtr_than.")";
die();
}
$table = $multTable->generateTable((int) $argv[1]);
echo $table . "\n";
I had a string variable in php with this value 000001422 and I used to modify it in order to display it in a better way:
<?php
$hits = "000001422";
$var = (int)$hits;
$var = abbreviateNumber($var);
echo $var; ?>
This is the function to crop and abbreviate the number in something like 1,4k
function abbreviateNumber(value) {
var newValue = value;
if (value >= 1000) {
var suffixes = ["", "k", "m", "b","t"];
var suffixNum = Math.floor( (""+value).length/3 );
var shortValue = '';
for (var precision = 2; precision >= 1; precision--) {
shortValue = parseFloat( (suffixNum != 0 ? (value / Math.pow(1000,suffixNum) ) : value).toPrecision(precision));
var dotLessShortValue = (shortValue + '').replace(/[^a-zA-Z 0-9]+/g,'');
if (dotLessShortValue.length <= 2) { break; }
}
if (shortValue % 1 != 0) shortNum = shortValue.toFixed(1);
newValue = shortValue+suffixes[suffixNum];
}
return newValue;
}
This worked great.
Now I'm trying to switch my code to AngularJS (I'm still learning it) so my value it's not a php variable but a scope variable received from an $http request. I have something like {{x.count}} which is my 000001422 but I would like to display it in the same way I did before.
I tryed to $hits = "{{x.count}}"; but it's not working and I think it's not the right approach. Can you address me in the right way to handle this?
EDIT:
I removed the initial zeros converting the value into an int with {{x.count - 0}} but still need to apply my abbreviate function.
Assume the following variable values were set earlier in the code:
LSLATHOR = 1780, NRSLATVER = 34
Then I have these two lines of GWBASIC:
100 PITCHHOR=(LSLATHOR/(NRSLATVER+1)) : LSLATHOR=PITCHHOR*(NRSLATVER+1)
110 IF PITCHHOR>72 THEN NRSLATVER=NRSLATVER+1:GOTO 100
120 LPRINT "HORIZONTAL PITCH is equal to : ";PITCHHOR;
Now if I wanted to put this logic as a PHP function how would I go about it?:
function calc_h($slat_length_h, $slat_qty_v) {
$pitch_h = ($slat_length_h / ($v_slat_qty + 1));
if ($pitch_h > 72) {
while ($pitch_h > 72) {
$v_slat_qty += 1;
$slat_length_h = $pitch_h * ($v_slat_qty + 1);
$pitch_h = ($slat_length_h / ($v_slat_qty + 1));
}
}
return $pitch_h;
}
$slat_length_h = 1780;
$slat_qty_v = 34;
echo calc_h($slat_length_h, $slat_qty_v);
What you need to know is that a condition will sometimes exist where PITCHHOR > 72 then it needs to adjust/re-calculate the $pitch_h according to the GWBasic script.
I hope I provided enough info. Ty vm.
I'd write as follows. But since you have the original code, you can simply try to plug in a few sample values and compare the results.
function calc_pitchhor($lslathor, $nrslatver) {
do {
$pitchhor = ($lslathor/($nrslatver+1));
$lslathor = $pitchhor*($nrslatver+1);
++$nrslatver;
} while($pitchhor > 72)
return $pitchhor;
}
$lslathor = 1780;
$nrslatver = 34;
echo "HORIZONTAL PITCH is equal to: ", calc_pitchhor($slat_length_h, $slat_qty_v);
I'm trying to convert the LZW decompressor from JSend which is in php to javascript, and I've gotten to a function I can't quite make sense of.
private static function decompressLZW($aCodes)
{
$sData = '';
$oDictionary = range("\x0000", "\xff");
foreach ($aCodes as $sKey => $iCode)
{
$sElement = $oDictionary[$iCode];
if (!isset($sElement))
$sElement = $sWord . $sWord[0];
$sData .= $sElement;
if ($sKey)
$oDictionary[] = $sWord . $sElement[0];
$sWord = $sElement;
}
return $sData;
}
This is what I have in javascript so far, but I when I run this in javascript, it complains that sWord isn't defined and looking at the php function, I don't see how this doesn't produce an error?
Here's what I have in javscript so far:
function decompressLZW(aCodes) {
var sData = '';
var oDictionary = [];
for (var i = 0; i < 256; i++) {
oDictionary[String.fromCharCode(i)] = i;
}
for(var i=0, iLn = aCodes.length; i < iLn; i++) {
var sElement = oDictionary[aCodes[i]];
if(!sElement) {
sElement = sWord + sWord[0];
}
//some magic needs to happen here
}
return sData;
}
Well its kind of bad IMO but.... $sWord is essentially $sElement which is defined near the end of the iteration. They are counting on the first two if statements be true only after the at least a single run of the loop in which case $sWord would be the same as the previous iteration's $sElement.
Im not sure what the significance of that assumption is but if it were me i would still test for the existence of $sWord and throw an exception if that happened (even if it should theoretically never happen)...
So you need to figure out why sElement = oDictionary[aCodes[i]]; isnt evaluating to something truthy. It may be as simple as testing angainst undefined (which is more like doing isset()) instead of doing loose falsy check.
if(sElement === undefined) {
sElement = sWord + sWord[0];
}
I have a MySQL table holding lots of records that i want to give the user access to. I don't want to dump the entire table to the page so i need to break it up into 25 records at a time, so i need a page index. You have probably seen these on other pages, they kind of look like this at the base of the page:
< 1 2 3 4 5 6 7 8 9 >
For example, when the user clicks on the '4' link, the page refreshes and the offset is moved on (4th page x 25 records). Here is what i already have:
function CreatePageIndex($ItemsPerPage, $TotalNumberOfItems, $CurrentOffset, $URL, $URLArguments = array())
{
foreach($URLArguments as $Key => $Value)
{
if($FirstIndexDone == false)
{
$URL .= sprintf("?%s=%s", $Key, $Value);
$FirstIndexDone = true;
}
else
{
$URL .= sprintf("&%s=%s", $Key, $Value);
}
}
Print("<div id=\"ResultsNavigation\">");
Print("Page: ");
Print("<span class=\"Links\">");
$NumberOfPages = ceil($TotalNumberOfItems / $ItemsPerPage);
for($x = 0; $x < $NumberOfPages; $x++)
{
if($x == $CurrentOffset / $ItemsPerPage)
{
Print("<span class=\"Selected\">".($x + 1)." </span>");
}
else
{
if(empty($URLArguments))
{
Print("".($x + 1)." ");
}
else
{
Print("".($x + 1)." ");
}
}
}
Print("</span>");
Print(" (".$TotalNumberOfItems." results)");
Print("</div>");
}
Obviously this piece of code does not create a dynamic index, it just dumps the whole index at the bottom of the page for every page available. What i need is a dynamic solution that only shows the previous 5 pages and next 5 pages (if they exist) along with a >> or something to move ahead 5 or so pages.
Anybody seen an elegant and reusable way of implementing this as i feel i'm re-inventing the wheel? Any help is appreciated.
Zend Framework is becoming a useful collection and includes a Zend_Paginator class, which might be worth a look. Bit of a learning curve and might only be worth it if you want to invest the time in using other classes from the framework.
It's not too hard to roll your own though. Get a total count of records with a COUNT(*) query, then obtain a page of results with a LIMIT clause.
For example, if you want 20 items per page, page 1 would have LIMIT 0,20 while page 2 would be LIMIT 20,20, for example
$count=getTotalItemCount();
$pagesize=20;
$totalpages=ceil($count/$pagesize);
$currentpage=isset($_GET['pg'])?intval($_GET['pg']):1;
$currentpage=min(max($currentpage, 1),$totalpages);
$offset=($currentpage-1)*$pagesize;
$limit="LIMIT $offset,$pagesize";
It's called Pagination:
a few examples:
A nice one without SQL
A long tutorial
Another tutorial
And Another
And of course.. google
How about this jQuery-plugin?
So all the work is done on the clientside.
http://plugins.jquery.com/project/pagination
demo: http://d-scribe.de/webtools/jquery-pagination/demo/demo_options.htm
Heres an old class I dug out that I used to use in PHP. Now I handle most of it in Javascript. The object takes an array (that you are using to split the stack into pages) and return the current view. This can become tedious on giant tables so keep that in mind. I generally use it for paging through small data sets of under 1000 items. It can also optionally generate your jump menu for you.
class pagination {
function pageTotal($resultCount, $splitCount) {
if (is_numeric($resultCount) && is_numeric($splitCount)) {
if ($resultCount > $splitCount) {
$pageAverage = (integer)$resultCount / $splitCount;
$pageTotal = ceil($pageAverage);
return $pageTotal;
} else {
return 1;
}
} else {
return false;
}
}
function pageTotalFromStack($resultArray, $splitCount) {
if (is_numeric($splitCount) && is_array($resultStack)) {
if (count($resultStack) > $splitCount) {
$resultCount = count($resultStack);
$pageAverage = (integer)$resultCount / $splitCount;
$pageTotal = ceil($pageAverage);
return $pageTotal;
} else {
return 1;
}
} else {
return false;
}
}
function makePaginationURL($preURL, $pageTotal, $selected=0, $linkAttr=0, $selectedAttr=0) {
if (!empty($preURL) && $pageTotal >= 1) {
$pageSeed = 1;
$passFlag = 0;
$regLink = '<a href="{url}&p={page}"';
if (is_array($linkAttr)) $regLink .= $this->setAttributes($linkAttr); //set attributes
$regLink .= '>{page}</a>';
$selLink = '<a href="{url}&p={page}"';
if (is_array($selectedAttr)) $selLink .= $this->setAttributes($selectedAttr); //set attributes
$selLink .= '>{page}</a>';
while($pageSeed <= $pageTotal) {
if ($pageSeed == $selected) {
$newPageLink = str_replace('{url}', $preURL, $selLink);
$newPageLink = str_replace('{page}', $pageSeed, $newPageLink);
} else {
$newPageLink = str_replace('{url}', $preURL, $regLink);
$newPageLink = str_replace('{page}', $pageSeed, $newPageLink);
}
if ($passFlag == 0) {
$passFlag = 1;
$linkStack = $newPageLink;
} else {
$linkStack .= ', ' . $newPageLink;
}
$pageSeed++;
}
return $linkStack;
} else {
return false;
}
}
function splitPageArrayStack($stackArray, $chunkSize) {
if (is_array($stackArray) && is_numeric($chunkSize)) {
return $multiArray = array_chunk($stackArray, $chunkSize);
} else {
return false;
}
}
}