Sign in to follow this  
menyo

[web] 2 player turn battle script crashes.

Recommended Posts

Hi folks,

If thought i finally managed to make a 2 player game with PHP and Jquery but i my whole computer gets stucked when the first player has gets his 2nd turn.

What i basically have are 2 .txt files where both players units are listed. I will read this file into an array. Each unit has a speed key wich value increases when that unit has made an action, i write the new speed to the file and the script runs again.

Heres the script that decides which unit goes first.


$time = 0;
$found = 0;
while ($found != 1)
{
foreach ($P1recruit as $p1)
{
if ($found == 1) break;
if ($p1['speed']<$time)
{
$p1['speed'] = $p1['speed'] * ceil ($time / $p1['speed']);
}

elseif ($p1['speed']==$time)
{
$p1turn = true;
$p2turn = false;
$found ++;
}
}

foreach ($P2recruit as $p2)
{
if ($found == 1) break;
if ($p2['speed']<$time)
{
$p2['speed'] = $p2['speed'] * ceil ($time / $p2['speed']);
}

elseif ($p2['speed']==$time)
{
$p1turn = false;
$p2turn = true;
$found ++;
}
}


$time ++;

}




Then follows all the HTML to build up the page. The player who has to act gets a form with choices to do with his unit. The other player gets "Opponents turn" in that window.

So in the above code you see $p1turn or $p2turn gets true or false. This is for the Jquery script at the end of all the HTML to tell wich player needs to keep polling the unit files until theres a unit for him to act. Heres the script:


if ($p1turn == true)
{
if ($battle['player2'] == $_SESSION['SESS_MEMBER_ID'])
{
echo "
<script type='text/javascript'>

function turnlistener() {
$('#main').load('battlegetnextunit.php');
};

$(function() {
setInterval('turnlistener()', 5000 );
});

</script>
";
}

}

if ($p2turn == true)
{
if ($battle['player1'] == $_SESSION['SESS_MEMBER_ID'])
{
echo "
<script type='text/javascript'>

function turnlistener() { $('#main').load('battlegetnextunit.php');
};

$(function() {
setInterval('turnlistener()', 5000 );
});

</script>
";
}
}
?>



Now for the first player (the one who starts the battle) he can act properly. The next player can act, but he still is longpolling the .txt files as the form resets. If he acts withing the 5 seconds (setInterval('turnlistener()', 5000 )) the other player gets his turn again but my whole computer starts strugling and i'm not able to select anything in the form.

Maybe it's like the players get double longpolling the 2nd turn. But i only have the last code once and obviously only 1 of 2 can run as either $p1turn or $p2turn can be true and it can only run for one player.

What could this be? Is doing it this way even possible? I'm trying to figure this out for days now, i really hope someone can help me out.

Thanks!

[Edited by - menyo on September 29, 2010 10:27:16 AM]

Share this post


Link to post
Share on other sites
First, a few things:

1) If you're going to post that much code, please use a code-paste site, such as http://codepaste.net/

2) Please continue to work on your coding capabilities (doing tutorials, etc). Don't take this the wrong way, but your code is border-line awful.

3) Why are you not using mysql/classes for this in lieu of text-files and arrays?

4) Congrats on starting a php game. I am a big fan of these.


Ok, onto the problem. Have you tried doing a reset() of $p1 and $p2 (which, btw's, I often refer to each player as $P[$i] in my combat code :P )? If you do not reset the array (ie, set the pointer back to the first row), you won't be able to walk it again with foreach() Not really sure how you're storing the array information, but I'd start here.

Also, you may want to add several 'echo' statements throughout your code so you can see where its getting hung at.

5) I'm putting this at the bottom because it may be a while off for you, but consider using a least-common-multiple function for finding the next players attack turn. If I have a speed of 3, and you a speed of 4, it makese 'sense' to let every '12th' point be the start of a new turn. In other words, I'd start with 3, you 4. I'd move up to 6, you 8. Then we'd hit 9 - 12, in which a turn event is triggered for you. This will save you several iterations during your combat sequence.

Share this post


Link to post
Share on other sites
Quote:
Original post by Cygnus_X
1) If you're going to post that much code, please use a code-paste site, such as http://codepaste.net/

I'd prefer it if he just used the source tags! (See the Forum FAQ.)

Anyway, I've no idea what the actual problem is, because there's this talk about "longpolling" (what does that mean?) of text files, except we don't get to see that code. Nor do we know what "battlegetnextunit.php" does.

There's no good reason why the whole computer would have a problem. Are you running PHP on the same machine that you're using the browser from? What does Task Manager say is taking up all the CPU time when this happens?

Share this post


Link to post
Share on other sites
We're still only 4 lines into your code and we have a variable that we don't know how it got defined.

$P1recruit

where does this come from?

Also, on a separate topic, the htmlentities() function doesn't always want to work right in the forums. It would be nice if someone could look into this.

[Edited by - Cygnus_X on September 29, 2010 10:32:34 AM]

Share this post


Link to post
Share on other sites
Srry, just read the Forum faq.

Also i used clearInterval without succes.

Anyways

@Cygnus_X
What you mean by borderline awfull? Do you mean the code is bad or how i format/layout it is bad? I know i have much to learn and i will :D.

I'm not yet familiar with classes, i will look into it. Why i'm using txt files? Actually i wanted to test out if it would work with txt files and increase my knowledge a little in that area. I made some tests and it seemed to work. I'm using JSON to write an array as an object to a txt file. then read it back in and convert it back to an array again. I was wondering what would be faster, query the DB for the 8 units each couple of second or reading out the file each couple of seconds. Don't know the answere yet as this major problem occured (topic).

So on to the problem. I don't think i need to reset() the array. I read it out from the file each time. It's like this battle.php loads getnextunit.php here the whole page is build up wich works fine then form battle.php gets from getnextunit.php is sent to battleaction.php wich does the math changes the array writes it back to the file and loads battleaction.php again. So the player won't leave battle.php. As this works for 2 turns i see no problem here.

I have put several echos into the code, but once it hangs the browser of the player who needs to act just crashes.

Not sure what you ment by 5) but i was going to sent the time to each page and include that into the txt file or database. I tested the code and if i manualy set the time to milions it still gave the next unit correctly and instant.


@Kylotan

Longpolling is a script that keeps running on the client with something like setInterval. In my case it keeps calling battlegetnextunit.php to look if theres a unit that belongs to him that is next to act. If so the page generates the command form.


Here is my complete code for the battle.
battle.php:
http://codepaste.net/1pbik4

battlegetnextunit.php:
http://codepaste.net/t42b8r

battleaction.php:
http://codepaste.net/etcqsi

Thx!

Share this post


Link to post
Share on other sites
Quote:
Original post by Cygnus_X
We're still only 4 lines into your code and we have a variable that we don't know how it got defined.

$P1recruit

where does this come from?

Also, on a separate topic, the htmlentities() function doesn't always want to work right in the forums. It would be nice if someone could look into this.


battle.php

$p1recruit = array();
while ($player1recruits = mysql_fetch_assoc($recruits1query))
{
$P1recruitquery = mysql_query ("
SELECT recruitID, name, type, health, stamina, energy, damage, defense, reaction, accuracy, speed FROM army WHERE recruitID = ".$player1recruits['recruitID']."
")or die(mysql_error());

$P1recruit[] = mysql_fetch_assoc($P1recruitquery);
}

$p2recruit = array();
while ($player2recruits = mysql_fetch_assoc($recruits2query))
{
$P2recruitquery = mysql_query ("
SELECT recruitID, name, type, health, stamina, energy, damage, defense, reaction, accuracy, speed FROM army WHERE recruitID = ".$player2recruits['recruitID']."
")or die(mysql_error());

$P2recruit[] = mysql_fetch_assoc($P2recruitquery);
}


if (!file_exists("".$battle['player1']."-".$battle['battleID'].".txt"))
{

//create file for player 1 recruits
$P1file = "".$battle['player1']."-".$battle['battleID'].".txt";
$handle1 = fopen($P1file, 'w+') or die("can't open file");

//Put player1 recruits array into a JSON string to write to file
$arraytojson = "".json_encode($P1recruit)."";
fwrite($handle1, $arraytojson);
//close file
fclose($handle1);
}

if (!file_exists("".$battle['player2']."-".$battle['battleID'].".txt"))
{
//create file for player 2 recruits
$P2file = "".$battle['player2']."-".$battle['battleID'].".txt";
$handle2 = fopen($P2file, 'w+') or die("can't open file");

//Put player2 recruits array into a JSON string to write to file
$arraytojson = "".json_encode($P2recruit)."";
fwrite($handle2, $arraytojson);
//close file
fclose($handle2);
}



battlegetnextunit.php

//open battle file for player 1, --> Make sure to delete file when battle finishes.
$player1 = file_get_contents("".$battle['player1']."-".$battle['battleID'].".txt");
$obj1 = (json_decode($player1));

$P1recruit = objectToArray( $obj1 );
/*print_r($P1recruit);
echo "<br><br>";*/


//open battle file for player 2, --> Make sure to delete file when battle finishes.
$player2 = file_get_contents("".$battle['player2']."-".$battle['battleID'].".txt");
$obj2 = (json_decode($player2));

$P2recruit = objectToArray( $obj2 );
/*print_r($P2recruit);
echo "<br><br>";*/




Share this post


Link to post
Share on other sites
opera seems to be causing the crashing as it is using over 1GB of memory. I have a crappy 4 year old computer with only 2GB of memory the other 2GB burned a year ago. But i'm planning on buying a brand new PC in a couple of weeks. But still that would not solve it.

Would the file handling be the problem? It loads the file each couple of second. It seems to do alright for a minute or 2.

-edit-
Well the opera thing is not happening every time. I am aware that the code isn't that efficient. I tried many many ways to get this to work and fix this problem.

I'm gonna scrap what i have and start over. Maybe read something about classes or try it just with polling the database.

Cygnus_X

Can you offer me some more insight on how you would make this type of turn based game? What i'm trying to achief here is that if unit 1 has 3 speed and unit 2 has 5 speed it would go like this: unit1(3) -> unit2(5) -> unit1(6) -> unit1(9) ->unit2(10)



[Edited by - menyo on September 29, 2010 11:21:48 AM]

Share this post


Link to post
Share on other sites
While reading over this code, I couldn't help thinking that you just need to start over. I'm glad you said it first. Storing data in a text file that you're constantly having to update, un-encode and re-encode is a bad design when you have an available database to work with.

Since you are unfamiliar with classes, I'll give you my explanation. As a pre-warning though, I'm self-taught much like yourself, and many of the users on these forums are many times more technical and knowledgeable than I.

The biggest benefit of classes, IMO, is that they offer superior variable management. Consider having a function called round() that rounds a number to the nearest integer. We don't care about how the code works, we just know the basic structure is:

function round($x)
{ ..... }

Lets say this is a good function for us, and we use it a million times over the course of 10 years. Then, one day, we decide that we want to round to the nearest tenth decimal place. We have two options:

Options 1 - change to function to round($x, $precision) and then re-write the function. The problem with this is that we'd have to change all previous 1 million instances of round($x) to round($x, $precision) to get the new code to work properly.

Option 2 - Create a new function. This option is better than option 1 for this example, but now we have 2 functions that do virtually the same thing.

So, here is where classes come in. With classes, we'd write this as:

class math
{
function --construct()
{ }

function round($x)
{ ... }
}

And we'd call this method via:

$Math = new math;
$MyVar = $Math->round('3.45');

Whats so great about this? Well, if we wanted to change the precision, all we'd have to do is add $this->precision = 0; to the __constructor function (ie, method cause its in a class), and for the next time we call it, we'd use:

$Math = new math;
$Math->precision = 1;
$MyVar = $Math->round('3.45');

The idea here is, the __constructor method gets called every time the class is defined. So, once we realize we need to add precision, we can go back and auto-define the default precision to 0. Then, when we need it, we can up it to 1 without having to worry about the million plus previous instances of the code. We do a little rework of the code inside round(), and bam, its works retroactively and has more functionality.


AS FOR THE ATTACK TIMER:

I'd set a clock inside my combat script (which I'd make as a class) that was the lcm * 2 of the two players speed. I'd then increment just as you did in a loop. I'd probably call the variable Initiative. And just do $P->Initiative += $P->Speed.

I'd then loop through all $P classes, get the highest initiative, compare it to the 'clock', then go again.

AS FOR YOUR CODE:

I'd have to see your design documents to lay this out better, but I'd set up a class that auto loads all the data it needs from the database. So, when you say:

$P[1] = new fighter($ID1);
$P[2] = new fighter($ID2);

These two lines of code can replace all the loops, etc, that you're currently using to load information out of the database and into an array. The possibilities only grow from here.



As I said earlier, I love php games. So i'd be happy to help you more with this (I have code you can look at) if you'd like.

Share this post


Link to post
Share on other sites
Since the browser is crashing after running out of available memory, your problem is in your ajax responses. You are either sending too much data or the JS is hanging somewhere. Try watching your network traffic and the live DOM to see what's actually going on during each load using Firebug or whatever dev tool you prefer.

Glancing over your code, you've got some strange things going on I don't understand. For instance, this block:


<script type='text/javascript'>

function turnlistener() {
$('#main').load('battlegetnextunit.php');
};

$(function() {
$(document).ready(turnlistener);
});

</script>




You keep resending turnlistener() unnecessarily. You only need it once.

Use the $(document).ready() method OR the passing a function as an argument to jQuery method, having both is messy and redundant. But actually, use neither: The initial page load can directly insert the opening battle sequence. Your second player should begin longpolling at that point, waiting for the first player to act. Your response to the first action should start that player longpolling, after the second player sees the action you should stop polling and allow him to act.

You have a lot of XHTML/HTML/CSS mistakes along with the goofy JS structure. Try removing all the backend logic to test your interface/ajax. Make a simple backend in PHP that simulates a typical battle (just generates the next stage of results whenever a player acts) and watch what happens.

You say you're using JSON, but you aren't. That's not how .load() works and not what battlegetnextunit.php responds.

I'm assuming your combat logic works (haven't looked hard at the code), your weak spot (and the bug) lies with your client-side code and server responses. Read over the jQuery API and some ajax/HTML/CSS tutorials to fix your logic and make your life easier.

Hope that helps.

EDIT:

While there are many reasons classes are good, the example Cygnus gave isn't one of them. The reason is option 3:

function round($x, $precision=0);

round() will continue to work as expected in the legacy code but now include a precision option. This is also how you would want to change your $Math->round() method, not by setting member variables in the constructor.

Rewriting your code to include a player class to get rid of all your duplicated loops and hard-to-read code is a spectacular idea. Game units lay very neatly into the objects that OOP is meant to express, take advantage of it!

Share this post


Link to post
Share on other sites



This might not be the problem, but you should probably consider changing
['speed']==$time
to
['speed']<=$time
just in case.

Share this post


Link to post
Share on other sites
@Cygnus_X: I would like it very much to take some insight into your code Cygnus_X. It would probably help me a lot both in less border-line coding and my own project :D.

@Kobo:
No because often the time of a unit is below the actual time. So if 1 unit has speed 40 and another unit speed 60 while time is 70 the unit that comes first in the array will act first instead of the unit with the least speed.

Share this post


Link to post
Share on other sites
Well i solved it.

The bug was indeed a looping bug as i already suspected. It was caused by long polling with setInterval to a page that redirected to the initial page that was long polling. I had to give the setInterval an ID and use clearInterval as each polling loop increased the amounts of polls expansional and therefor after a minute it was polling the page a couple of times each second :D.

I started over everything and it works great now! The code look better too although i did not yet used classes. Heres how i did it.

If it's opponents turn.
		
<script type='text/javascript'>

loader = setInterval(function() {
$('#command').load('battlewait.php');}, 2000);
</script>



and for the player that needs to act.

if (typeof(loader) != 'undefined') {
clearInterval(loader);
};




I had to use the if statement for the player that acts the very first as it would not have the loader defined.

Thnx for support, i will deffinatly look more into your code Cygnus_X as i don't understand some critical parts of making classes. However i do get the bigger concept on how powerfull they are.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this