Trouble understanding php function for LZW decompression in JSend - php

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];
}

Related

PHP OO - Associative array of object instances, resetting on each click

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.

Modify scope variable

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.

PHP, Function breaks when numbers are passed in as arguements

I have the following function which works well, if I call it with only the first parameter:
function max_months($vehicle_age,$max_peroid,$no_older) {
$tot_age_in = $vehicle_age + 315360000;
while ($tot_age_in > 536467742) {
$tot_age_in = $tot_age_in - 31536000;
if ($tot_age_in < 536467742) {
$max_payback = floatval($tot_age_in - $vehicle_age);
$max_payback = seconds_to_month($max_payback);
break;
}
}
return $max_payback;
}
However, when I alter this function and pass in the numbers seen above as
parameters, the function breaks.
function max_months($vehicle_age,$max_peroid,$no_older) {
$tot_age_in = $vehicle_age + $max_peroid;
while ($tot_age_in > $no_older) {
$tot_age_in = $tot_age_in - $max_peroid;
if ($tot_age_in < $no_older) {
$max_payback = floatval($tot_age_in - $vehicle_age);
$max_payback = seconds_to_month($max_payback);
break;
}
}
return $max_payback;
}
I'm calling the function like so:
$max_payback = max_months($vehicle_age,315360000,536467742);
$vehicle_age is set to 288897248
So in the first instance I return a valid number, however in the second instance I return false, even though the numbers are the same. Could anyone suggest why this might be? Cheers
$max_payback is not always initialized. It's a good habit to always initialize the return value..
It is highly likely that you run out of the PHP_INT_MAX value, you can check the maximum integer value by doing
echo PHP_INT_MAX;
If the variable is bigger than the INT_MAX, it is treated like a float value. This means, that you have to deal with floating point imprecision problems. And instead of checking <, == or >, you should check for a certain range epsilon around the value to be checked.
By changing your code like below, the problem is likely solved:
function max_months($vehicle_age,$max_peroid,$no_older) {
$e = 0.0001;
$tot_age_in = $vehicle_age + $max_peroid;
while ($tot_age_in > $no_older-$e) {
$tot_age_in = $tot_age_in - $max_peroid;
if ($tot_age_in < $no_older+$e) {
$max_payback = floatval($tot_age_in - $vehicle_age);
$max_payback = seconds_to_month($max_payback);
break;
}
}
return $max_payback;
}
See also: http://php.net/manual/en/language.types.integer.php
You did not have that problem when you used the hard coded numbers because they are treated like constants and therefore you did not have the float problem.

Undefined Index (Laravel)

I'm bashing my head against my desk trying to figure out why this PHP code is causing this error: Undefined index: arr. I'm using Laravel, and this code works like gold outside of it, but inside Laravel, it's returning the undefined index error.
Here's the code:
function set_pilots_array($line_array)
{
$airports = $this->airports;
$pilots = $this->pilots;
foreach($airports as $airport)
{
if($airport == $line_array[11] || $airport == $line_array[13])
{
if($airport == $line_array[11])
{
$deparr = "dep";
}
if($airport == $line_array[13])
{
$deparr = "arr";
}
$this->pilots[$deparr][] = array($line_array[0], $line_array[11], $line_array[13], $line_array[7], $line_array[5], $line_array[6], $line_array[8]);
}
}
}
function get_pilots_count()
{
$count = count($this->pilots['dep']) + count($this->pilots['arr']);
return $count;
}
This sort of goes with my other question: Grab and Explode Data It's pulling the data from the data file using this code:
elseif($data_record[3] == "PILOT")
{
$code_obj->set_pilots_array($data_record);
}
Which later does this:
$code_count = $code_obj->get_pilots_count();
You do not have $this->pilots['arr'] set. In other words, if you look at the output of var_dump($this->pilots);, you shall see there is no arr key-value pair. I suggest you this fix:
$count = count((isset($this->pilots['dep']) ? $this->pilots['dep'] : array())) + count((isset($this->pilots['arr']) ? $this->pilots['arr'] : array()));
Actually, this is not a fix - this is more like a hack. To make your code correct i suggest you to set the default values for those $pilots['arr'] and $pilots['dep'] values:
function set_pilots_array($line_array)
{
$airports = $this->airports;
$pilots = $this->pilots;
foreach (array('dep', 'arr') as $key)
{
if (!is_array($pilots[$key]) || empty($pilots[$key]))
{
$pilots[$key] = array();
}
}
// ...
}
Well there is too little code to really figure out what is going on, but based on what I see:
if($airport == $line_array[13])
this condition is never being met and so $deparr = "arr"; never happens and because of this
count($this->pilots['arr']);
is giving an undefined index error
You can easily suppress this by:
$count = count(#$this->pilots['dep']) + count(#$this->pilots['arr']);
your problem is that you are accessing all of your indexes directly without checking if they exist first.
assume that in laravel something is causing the array to not be populated.
in order to fix this, you should either iterate through the array with a foreach, or do a if(!empty($line_array[13])) {} before accessing it.

Performance: condition testing vs assignment

I've created a loop where a variable is used to test if the current run-through of the loop is the first one. Its fairly simple:
$firstrun = true;
while(condition){
if($firstrun)
// Do this
else
// Do that
// Change $firstrun to false
}
I was just wondering (mostly out of curiosity because I'm it makes no real noticeable difference), when I need to change $firstrun to false, would be more efficient to test if the variable is true before assigning it to false or simply reassign it to false during each run-through?
Ex:
$firstrun = true;
while(condition){
if($firstrun)
// Do this
else
// Do that
if($firstrun)
$firstrun = false;
}
or simply
$firstrun = true;
while(condition){
if($firstrun)
// Do this
else
// Do that
$firstrun = false;
}
PS:
I guess this is a bad example also, because it would be most efficient to throw the reassignment of $firstrun in with the original condition, but as I said this is out of curiosity so I guess just pretend that is not an option for some reason.
PSS:
I was coding in PHP when this idea hit me, but I'm guessing the solution would be language agnostic. Just thought I would throw that in there in case it does for some reason matter.
So ultimately, which is faster, condition testing or variable assignment?
none of the above
$firstrun = true;
while(condition)
{
if($firstrun)
{
$firstrun = false;
}
else
{
}
}
reason I said so, because you are repetitively re-assign false to $firstrun, which you should just do at the first loop
condition test vs assignment which is faster?
for example you have shown, is the same (one execution cycle without some expensive call)
updated
I think condition testing will be slower, cause you might invoke series of subsequent action after that
This could be better, depending on what condition actually is:
if (condition) {
//execute first run code
while (condition) {
//execute subsequent run code
}
}
Given your example, you don't need the extra variable.
You don't even need the if statement if you know the code will always run at least once:
//execute first run code
while (condition) {
//execute subsequent run code
}
<?php
class Test
{
private $var = 156135135;
const SOMETHING = 156135135;
public function assign()
{
$this->var = self::SOMETHING;
}
public function conditionalAssign()
{
if ($this->var != self::SOMETHING) {
$this->var = SELF::SOMETHING;
}
}
}
$obj = new Test;
$start = microtime(true);
for ($i = 1; $i < 10000000; ++$i) {
$obj->assign();
}
echo round((microtime(true) - $start) * 1000, 2).' ms'.PHP_EOL;
$start = microtime(true);
for ($i = 1; $i < 10000000; ++$i) {
$obj->conditionalAssign();
}
echo round((microtime(true) - $start) * 1000, 2).' ms'.PHP_EOL;
conditionalAssign always faster when variable is integer, often faster when variable is boolean and almost equal, when variable is string.

Categories