Tuesday, May 11, 2010

Pre-submission goals, update #4: About, Help, ...and that's it.

I decided at this point that I've covered all the important/major bugs and enhancements that I wanted to work on between presentations and submission, so the last thing I did was to finally add the "about" and "help" sections.  (Yes, those links actually didn't do anything until perhaps an hour and a half ago.)

In lieu of creating actual separate pages for these sections -- which could severely disturb the logged-in/logged-out status management and gameplay (in the case of people jumping to these pages who knows when) -- I added an "about" div and a "help" div to both the index.php and playgame.php pages, with the same content on each page.  Both divs are hidden when you arrive on the page, but thanks to jQuery, when you click the links up in the header, they will slide into view.  Yes, both can be viewed at the same time, but I think that's fine.  Each div has a little [x] in the upper right; clicking on this slides them back into hiding.  The rest of the page content slides down to allow them to appear (i.e. they don't cover the main page content).  I like this setup because you can view the information whenever you want while using the app.  This allows for handy referral to game instructions even while playing!

I'm pretty tired... moving on to final documentation.

Pre-submission goals, update #3: So, what happens when I fly off the grid?

There is one significant issue I have found (via user testing) that occurs in my Flash version of this game that I do not recall ever occurring when I played this game on paper with my dad.  This would be the possibility of players going so fast that not only do they go out of the track boundaries, but they end up in the position in which all options for their next move are off the playing grid entirely.  I wonder if my discovery of this is a result of my throwing my friends into playing my game without their really having a clue what they had to do, and thus having pretty much no clue (initially) of how to strategize the management of velocity.  But either way, I thought that it would be fairly bad in terms of user experience to force everyone to restart the game if a player went off the grid, so I decided to handle that case.

I consulted with my dad, and he helped me come up with a reasonable solution.  I decided that if a player comes to their turn and all their options are off the grid, that move will be taken for the player as follows:
  1. They will move to the "middle" option (i.e. the gridpoint that would maintain both their x- and y-velocities), wherever it may be, off the grid.
  2. Then, they will "slide" back onto the edge of the grid.  In other words, if they are off past the top or bottom edge, their y-coordinate will be adjusted, and if they are off past the left or right edge, their x-coordinate will be adjusted.
  3. As a "punishment" for going off the grid, their velocity will be reset to (0, 0).
Since the player has no control of this move that's bringing them back onto the grid, they are informed of it, like so:


And on your next turn, you start off at the edge with (0, 0) velocity:

Sunday, May 9, 2010

Ongoing Compendium of Resource Links

As a way of bookmarking and categorizing online [potential] resources I will update this blog post throughout my project.

Update: I am now going to mark the newly added links for each update of this post by highlighting them.



Facebook-application-related  --------------------------------------------------------------------------





Wiki - Cross Domain Communication Channel - so that my pages (within the iframe) can talk to the parent (Facebook-hosted) page.  This is necessary for using the JS client library.

* (See the other post about the Facebook platform integration for a much longer categorized list of links...)


PHP + MySQL  --------------------------------------------------------------------------



PHP - Specific functions etc.  --------------------------------------------------------------------------






mysql_query - includes links to related functions: mysql_fetch_array, mysql_num_rows, mysql_affected_rows

mysql_real_escape_string - apparently you should use this on your query strings before sending them


include - this documentation also applies to 'require' (and mainly to 'include_once' and 'require_once')

Boolean (type) - with useful comments on various funky details of comparisons, casting, how the representation is output


PHP & Flash  --------------------------------------------------------------------------




Cron Jobs  --------------------------------------------------------------------------

Running PHP cron jobs – regular scheduled tasks in PHP

Cron Job setup on Webhostingpad


Javascript  --------------------------------------------------------------------------

Accessing PHP variables in Javascript - a simple issue, but I needed to know :P

AJAX GET and POST examples - mostly because I wanted to remind myself of the format of the parameters string, and didn't realize the content-type had to be set for the POST to indicate it would be in name/value pair format if I was sending a string I constructed.

Setting and clearing timed events - for AJAX polling for database changes



Flash [games]  --------------------------------------------------------------------------


Flash/Actionscript [basic/reference]  --------------------------------------------------------------------------


Flash CS4 Migration Guide from AS2 to AS3 - I expect this to be quite useful when I'm Googling around for random issues I encounter in Actionscript and I find blog posts and whatnot that happen to use AS2 for code examples.  Credit to John for finding this. :)








Flash/Actionscript [specifics]  --------------------------------------------------------------------------

-- The Actionscript Language --


(since there's no native support)










-- Relevant AS Classes --



flash.display.Graphics - access programmatic drawing functions (lines, circles, fills, ...)



-- Buttons --






-- Movie Clips (and other DisplayObjects) --





^ extensive tutorial. I like this one.

This is a multi-page/many-step tutorial... haven't looked at it much.

One of those long multi-page tutorials, but this might have some useful bits in there. (Haven't looked at it much yet.)



scroll down and try what's mentioned at the end, maybe


-- Event handling --

(oh yeah! I remember using this same structure in Javascript...)




-- Actionscript + external code functionality --

[AS3] Forum thread: "Sending variables from AS3 to php and back to AS3" - really helpful posts about how to set up the necessary code pieces (the Actionscript and the PHP), with code snippets

[AS3] "Working with external data" - article with code examples about using the URLVariables and URLLoader classes, pulling from external documents, and accessing external scripts


-- Drawing --

This looks like AS2, but the code structure gives me the basic idea of how to use the drawing API functions together to create a simple drawing app.




-- Edges/intersection --




-- (uncategorized) --




Oh hey, detecting and updating user presence is ridiculous. ( = pre-submission goals: update #2)

An annoying behind-the-scenes bug turned out, as I've learned after many hours of struggle today, to be far more frustrating and complicated than I could have imagined.

In my Users table in the database, I simply store a boolean, 'logged_in', by which I intend to record which players are currently using the app.  This does not mean whether they are logged into Facebook, since that status is likely unrelated to my app.  So, I define my logged_in boolean to mean whether the user is on either the index (home) page or the playgame.php page.

It's simple enough to ensure this boolean is marked true when the user loads either of these pages.  Detecting their leaving a page, however -- with the added requirements of updating all contingent information -- is unbelievably far more difficult.

Concerning the latter note, if a user leaves a game while it's in progress...
  • They should be marked 'logged_in' = false, since if they're navigating to one of the app pages, that page will restore that boolean to true.
  • Their 'current_game_id' and 'current_pid' variables must be set to null, to indicate they are no longer in that game.
  • If they were the last remaining player in that game, all turns pertaining to that game as well as the game's entry itself must be cleared from the Turns and Games tables, respectively.
  • If there are any other players left in that game, the 'num_players' value for that game must be set to the new correctly decremented value (and I don't want to just subtract 1, in case of race conditions, so I always want to check with how many players are still marked as involved in the game based on 'current_game_id' in the Users table).
  • The other players in the game should be checking for whether their fellow players have left, and must be informed of such dropouts.
 My original solution to detecting a player leaving the game page was to register the window.onunload event to make an AJAX POST to a php script that would update all appropriate things (see the list above).  This worked well enough in Firefox; I checked the results in the database, and Firebug showed me that the POST was indeed going through, despite the fact that the browser was navigating to another page.  However, I ended up discovering that the window.onunload event, or perhaps the POST, wasn't going through fully in Safari.  This led me to realize I should try an entirely different approach that would not rely on a browser event that may not have solid implementation in all browsers...

Many thanks are due here to John for helping me form new ideas for how to make this work.  Without going into the painful details and resulting unfortunate spaghetti-fication of my code (err..), the basic solution architecture is in three components:
  • The Users table now has a field called 'activity_time'.  Every 30 seconds, whether a user is on the home page or the gameplay page, an AJAX POST calls a php script to set this field to the MySQL CURRENT_TIMESTAMP.  So this is saying "user x is still here".
  • While on the gameplay page, the same script additionally checks for (a) any users marked with that 'current_game_id' but whose 'activity_time' is more than 30 seconds old, and (b) any users with one of the 'fb_id's registered (in a Javascript object stored by the page) as involved in that game, but with 'current_game_id' set to null.  [Note: (a) condition = those who have left the app by going to another page outside of it or closing the browser tab/window, and the cron job has not yet cleared up their data; see next bullet...  (b) condition = those who moved back to the app home page, which clears up their game data immediately, or those who have been cleared up already by the cron job.]  The id's of these "dropout" users are sent back to the page; the display of players in the game is updated to remove the dropouts, and a message is given to the user to advise them to return to the home page and start another game...(see below *).
  • There is now a cron job set up on the server to run every 5 minutes.  It calls another php script which retrieves all users whose activity time is more than 50 seconds old, removes any current game data they have, marks them as logged out, and either decreases the number of players in the game they had been in, or deletes that game entirely if it's now empty of players.
(*) Right now, there is no handling for updating the game state as it's stored in the ActionScript within the Flash app instance, nor does the page stop polling for the dropout players' turns.  As I see it, there are two options for where to go from here, regarding this issue:
  • Don't bother fixing the AS game state.  Just redirect the user automatically back to the home page.  This would make sense anyway for games that only had 2 players to start with, since playing alone is not as much fun.
  • Use the ExternalInterface class in AS to receive a notification from the page (via JS); update the game state accordingly; allow play to continue without the lost player(s).
Given how much trouble logout status has given me for many hours today, I'm inclined to go with the first option (or just let the user figure out that it doesn't work, and that they need to manually go back to the home page or leave the app).  It might turn out to be pretty quick to implement the second option, as long as there is still at least one player in the game, but I would rather turn my attention and remaining time towards a couple of the other goals on my pre-submission list, since I do think they are still important.

Finally, finally:


(I wonder how many Pandora hours I've used, out of my 40 hours given for the month, just from working today...)

Saturday, May 8, 2010

Pre-submission goals: update #1

Things fixed or completed so far...
  • The current velocity for each player is now displayed next to the thumbnail of their Facebook picture and is updated after each turn.
  • Players now cannot occupy the same gridpoint at the same time.  To do this, I simply don't display the little circle that indicates that you can move to a point if another player's piece is currently located on that point.  (I had one hitch initially -- I forgot that if your current velocity allows, you sometimes can stay on the same exact point you're already on, so I didn't want to hide the circle if it's on top of your own piece.)
  • I believe I have now fixed the bug of move-option circles not appearing when they should have, during the case of both (out of 2) players being out of bounds at the same time.  Luckily, it was a simple matter of updating a boolean that pertained to the other player; as in, I'd forgotten to update that bit of game state across all player instances (not just the instance of the player whose turn it had been).
  • (??) As John is graciously allowing me to test via his Facebook login, I was able to play a couple of full games (by logging in as myself in Firefox and as John in Safari) on my own screen, and the winner notification worked in all cases.  So maybe this is not a bug after all?
A thought I just had: I should really test this on more than just two players.  I don't think I ever really have.

p.s. Screenshot of preventing players from occupying the same point, without removing the option circle that coincides with your own current position:

Sunday, May 2, 2010

Goals prior to final submission

So after my first full night ever in the SIG lab, I managed to pull off my presentation pretty well, I think.  I even ran a live demo, playing against John, and nothing broke. (!!)

Post-presentation, I've still got about a week to wrap this project up.  Here's what I'm hoping to get done in that time:
  • (bug) Fix/rework the way user logout is recorded in the database... right now I keep finding that users are still marked logged-in when I don't think they are.  (At least login is working right, I think!)
  • (bug) Make sure the "player __ won!" popup works for all players.  I seem to remember a couple of test runs in which only one out of two players saw it when someone had won.
  • (feature) Show each player's current x- and y-velocity somewhere next to their picture.  This should be really easy to do and would be helpful to see.
  • ("feature") Write the About and Help pages (including gameplay instructions)
  • (enhancement) Spruce up the (logged-out) intro page and the main page with a short intro to the gameplay.
  • (enhancement) I kind of forgot to implement preventing players from occupying the same grid point at the same time.  This also should be extremely easy to implement.
  • (bug?) I just today played the game against a friend and we ran into the case in which both of us were out of bounds at the same time.  I think there might have briefly been a mix-up regarding displaying the move options for one of us.  (Gameplay still proceeded properly though, I think.)  So I should test this again and fix if needed.
  • (bug) I nearly forgot this one... but I really should do something about the case of players running off the edge of the entire grid.  Apparently this happens more easily in my online version than on grid paper with a pencil, haha.  (See previous blog post.)
  • (enhancement) Show more clearly when another player takes their move.  I'm thinking I should highlight the position circle they've chosen for about a second prior to turning off those circles and showing their piece in the new position.
  • ("enhancement") Write some back-logged blog posts.
I don't think any of these changes are too major, and none of them significantly change the gameplay, which was certainly functional by presentation time.

Will update this list if I think of anything else.

Saturday, May 1, 2010

Double fail: another full game played against a friend

Things I learned from this game:
  • I really need to do something about people flying off the edge of the entire grid.
  • I also really need to write an instructions page.  It needs to be really clear about how your acceleration works...
  • I need to double check what's going on when two players are out of bounds at the same time.  The flow of turns seemed to happen properly, but I think at one point my opponent's set of move-option circles didn't appear on my screen when they should have.  (I guess they showed up on his though because he was able to take his move.)
  • Also, Matt exploited his out-of-bounds moves to jump back into the track by just cutting straight through, whereas I basically slowed down to almost zero speed and turned around.  I'd actually never thought of his strategy before.  It's not exactly a bug that he was able to do this, and I'm not sure how to curb it in any simple way in the code, either...
Now for the fun screenshots...

Game 1 (well, the attempt):
(filename: matt_fail.png)



Game 2
(filename: tory_fail.png)

Thursday, April 29, 2010

Considerations extracted from first (real-..)game chat

...with additional thoughts spurred from which...

Interface
  • show players (names, pics, association to colored piece)
  • display whose turn it is (and note "your turn" or "waiting for player __")
  • display your current speed (and others' speeds?)
  • and re: current speed - show a little compass of -x/+x and -y/+y directions

Instructions

How to interact with the interface:
  • click on circle to select move
  • gameplay is turn-based so you must wait for other players' moves
  • other players' circles are NOT clickable to you when it's not your turn

Gameplay:
  • goal is to be first around the track
  • how your speed is allowed to change for each turn, and that the game calculates this for you and shows you your available moves
  • when you go out of bounds, you lose every other turn for the duration of turns you are out of bounds
  • boundaries of track (both inner and outer) are considered in-bounds
  • cannot occupy same gridpoint as another player at the same time, but can occupy another player's previous gridpoint

Implementation stuff
  • handle out-of-bounds (lose every other turn)
  • handle off-the-grid
  • detect when a player crosses the start line again (i.e. the winner!)
  • don't let players be on the same point at the same time
  • display something differently about the optpos when it's out of bounds

First game played against another real person!!

My wonderful friend volunteered to test-play my game!  I happily report that everything worked as it should, and we were able to play the game as fully as possible given the state of the code.  (Some things are missing, including: losing a turn when you're out of bounds, detection of a player winning the game, detection of a player going off the edge of the grid entirely -- this last issue did not come up in our game, but it's rather important to make sure I can handle that somehow.)

First I'll post a screenshot of our finished game.  I'm the blue player and my friend is the red.

Following the screenshot, I'm including a transcript of our chat conversation during the game (with my friend's permission, and mildly edited for relevance).  It's very helpful to know his thoughts during gameplay, particularly in the areas of: the rules of play, interaction with the interface, and information to be displayed during gameplay.

Our game:



Chat transcript:

me: alright let me instruct you what to do
friend: ok
me: you're still on the page?
friend: absolutely
me: ok refresh it
me: and you should see "game #20" on available games to start
friend: yeah click it?
me: i mean to join
me: yes
me: ok i can see tha tyou joined lol
friend: i'm in baby
me: now does the track show up?
friend: yes
me: alright it's my turn lol
me: i'm blue and your'e red
friend: how do i move it?
me: you have to wait for me to go first
me: i'm player 0
friend: ok
me: you're player 1
me: cuz i started this particular game
friend: ok
me: do you see my move?
friend: you moved up one space
friend: yeah
me: yes
me: now you can click any of the red circles to move there
me: we're trying to go around the track. lol
friend: i gotcha
me: so that's all... lol
friend: can we occupy the same space?
me: not at the same time
friend: ooh
friend: intersting
me: but you can only select from the circles to move to
friend: can i go right on top of the inner black line?
me: they show you where you can move based on the rules
me: yeah that should work
friend: ok
friend: your move
me: yes i know
me: i'm trying to explain lol
friend: hehe
me: cu zi realized you don't know the rules
me: but yeah it's pretty simple
friend: i gotcha
friend: oh now we can move two spaces up
me: the rule is
me: on each turn, you can change your x speed by -1, 0, or 1
me: and same for y speed
me: make sense?
me: so try not to go out of bounds
friend: so how do i change the speed?
me: you are changing it by selecting one of the option circles
friend: ahhh i see
me: middle circle = no change
friend: i getcha
friend: like a turn-based game
me: all the others are a change of some kind, in one or both directions
me: it is turn based yes lol
friend: i dont see why it's letting you take the inside route
friend: and why the spaces closest to the edge wasnt available to me
me: you always have an x/y speed.
me: like i said
me: in each turn you can change one or both of those by: -1, 0, 2 for each
me: but the game is doing the work of tracking your speed and available changes by showing you the options for your move
me: that's all haha
friend: ok fine
me: do you understand it?
friend: for now
me: you manage your acceleration
me: so you should start slowing down in the x-direction
friend: so what's the advantage of a slowing down
me: lol....
me: if you keep going so fast in x-direction
me: you won't be able to stay inside
friend: i getcha
me: b/c you can only slow down by 1 each turn
friend: ahhh ook ok
friend: play on
me: oh shit
me: lol i'm out of bounds hahha
me: right now i don't actually have any implementation for handling that case.
friend: so what does that mean, game over for you/i win?
me: no
me: i haven't coded this part yet
friend: oh ok
friend: so it's stuck
me: but when yor'e out of bounds you lose every other turn
me: so you get slowed down a lot in comparison to the other players
me: no it's not stuck i can just move normally haha
me: see the upper right, the game knows i'm out of bounds
friend: oh ok
me: but it's not going to do anything about it
me: sorry haha i'm technically cheating right now
friend: ahh i see
friend: ok i'm trying to go more down and less righgt
me: mhm
friend: so was that the right move to make
me: yeah that's the right strategy for the moment
me: but don't go flying downwards too fast
me: cuz you will have to slow down again
friend: cause i gotta make the turn
me: it's acutally harder than it looks lol
friend: i go tit,
me: yay
friend: lets see if i can do it
friend: haha
me: dude, you have no idea, i'm so excited right now
friend: ok so one thing
friend: i gotta start goign left
friend: how do i tell it to do that
me: you can't "tell" it to do anything
friend: right
me: you can just choose from the options
me: you'll see
friend: well how do i change my velocity
friend: to go -x
me: it'll just do it for you
friend: based on what
me: as long as you are selecting one of the left-hand-column circles to move to, you're changing your horizontal speed by -1 each time
friend: ah haa
me: just keep clicking lol
friend: ok
me: now your'e out
me: of bounds
me: but you can keep playing like i did lol
friend: yeah
friend: didnt have much of an option
me: see you are going -x now
friend: yeah i see
me: but now watch the inside bound
friend: it's just
friend: reactive
me: haha
me: it's like driving
friend: how do i know what my current "x" is
me: right now you have to count it, sorry
me: i'll make a display for that
friend: haha ok
friend: that's the only thing
friend: keeping track
me: yeahh sorry!
friend: shoulda slowed my Y a bit sooner
me: lol, yeah
me: oh and please try not to go off the board
me: i don't have anything to deal with that right now in the code lol
friend: i'm working on it
friend: but i kidna dont remember how much -Y i put in
me: you can count from your previous dot to your X now
friend: oh forget that
me: okay...
me: crap i might go out
friend: hee
me: i didn't slow x early enough
friend: you nooob
me: right sure.
me: yeah shit
me: haha i'm flyyying off the track there.
friend: hahah
me: you should aslo slow x now
me: by the way it's not goign to detect the winner..
friend: yeah i finally see the x slowing
me: ok i'm gonna stop there ok?
friend: ok so you won then
me: well i was out of bounds so
me: who knows haha
me: hey
me: can i ask you a huge favor
friend:
so it's supposed to skip yoru turn
friend: but it seem like it didnt
me: i know, i told you i didn't code the turn-losing yet
friend: oh ok
me: yeah..
friend: sorry osrry
me: lol it's ok
friend: just trying to get it striaght
friend: ok huge favor
friend: go ahead
me: yeah def.
me: so would you mind if i posted (most of) this conversation with you on my project blog?
friend: sure
me: this is the first time i've played wiht someone else on another computer.
me: i won't put your name
friend: just, know that i'm not educated
friend: haha
me: lol,
me: i like our conversation b/c you pointed out stuff that would be confusing to someone who's never played before
me: which are things i should consider when i write the instructions, etc
friend: ah i see

me: thank you soo much!

Tuesday, April 27, 2010

The Facebook API battle/disaster night

I thought it would be fairly simple...


I had worked on a Facebook iframe application once before, and I remembered that the addition of requiring Facebook login and then being able to render XFBML components was not very complicated at all.  It was a matter of pasting in some code in a few different places, and magically, the page would request you to log in and add the application, and then it could show you your Facebook picture and name.  (By the way: FBML = Facebook Markup Language, and XFBML is the version you can use on off-site pages, i.e. your pages in an iframe, or on Connect app pages.)

Well, there I was, just over a year later.  I began delving into the wiki/documentation on authentication etc., and what I began to realize is that everything seemed to be in transition.  There was this new "graph API", and OAuth authentication, and a new Javascript API -- or at least, these all appear to be new, based on the look of the pages and what I could gather from the descriptions.

I really only wanted to be able do 3 things: require login and authentication of the app, pull/display users' names, and pull/display users' profile picture thumbnail.

Many long hours of confusion later, I had gone through stages something like this:
  1. using the wrong new stuff (a PHP API which I now think is supposed to be for Connect apps?);
  2. trying to use the old PHP library, cross-domain receiver file, XFBML, and iframe resizing... I don't even remember what didn't work in this combination, but some piece didn't;
  3. trying to combine the old PHP library and XFBML paste-in code with some of the newer stuff, I think? I don't even remember;
  4. attempting to manually implement the OAuth redirection cycle (things like obtaining a key and this and that), which actually almost worked...;
  5. deciding to give up on making this an iframe application that's actually within the Facebook chrome, and instead, switching it over to a Connect app, using the easy authentication/login via the new Javascript API and all of which is luckily still compatible with XFBML.
...wow.  It was a long, tiring, frustrating struggle.  Much of it was due to over-documentation, if you will, and a lack of clarity in terms of marking pages regarding at which level of API/platform transition the information and code on that page would still be relevant and functional.


And now for the bombardment of a list of links.  Here are some of the many Wiki pages/sections I tried to use to figure this out...

(Note: seems like they are really in transition currently - some of the links may not work or will give a message saying the content is moved/updated)


New (pages are in the theme of the new documentation)

Overview guide to apps (new)

Authentication (new)

Graph API (new)

JavaScript SDK (new)

Old REST API - apparently the precursor to the new Graph API


Old, but seems to still apply / be compatible with new stuff

XFBML (including tag list)


Old, and seems to not apply / not be compatible with new stuff

API (old?)

PHP (old?)

FB_RequireFeatures (old?) - for dynamically loading components of the JS API (via FeatureLoader.js.php)

Authorization and Authentication for Canvas Page Applications on Facebook - matches with the old(?) PHP library

Extended Permissions

Connect/Setting up your site - including XFBML rendering (but uses the FeatureLoader.js.php)

Friday, April 23, 2010

Features to implement by presentation day

A rough list for the moment; probably to be updated as I go...

Definitely:
  • Select from 4 different provided tracks to play on
  • Communication between Actionscript and Javascript on the page, so that various information about the game state (whose turn it is, players going out of bounds, etc.) can be displayed on the page... This will replace the "log" text box I currently have sitting in the middle of the track.
  • Players should lose every other turn while they are outside the track bounds -- see this discussion.
  • Displaying lines (options thereof?) connecting the dots of players' previous moves (maybe just the last move, to highlight it; probably all moves once the game is over, to show the overall trajectory)
  • Finish the database setup for real... because so far I've just been adding fields and tables along with adding corresponding features to the game.  I should probably redraw/re-post a new ER diagram too.
  • Facebook login required to get to the app (because right now I'm pretty sure you can just go to the page and see it); and consequent storage of app users in the database, including whether they are currently online.
  • Display on the main (index) page of your Facebook friends who are currently logged in to the app
  • [maybe] primitive inviting of your friends to join games
  • Obviously, the entire interface needs a nice layout and lots of CSS...
  • Some sort of about/help page(s)
Optimally would be nice to have these, but no guarantees:
  • AI opponent so you can play single-player
  • allow players to draw their own tracks
  • ...and save them for subsequent use?
  • public/private games or allowing the game organizer to moderate (real-time) other players' joining

Wednesday, April 21, 2010

Multi: -player, -game, -track support

Comprehensibility of the following bullet-point lists is questionable, unless you're me and therefore intimately familiar with this project's code...

Multiplayer support:
  • Games support 1, 2, 3, or 4 players.  Having just one player right now makes no sense because there is no AI opponent.
  • Gameplay page polls (Javascript AJAX calls) database to discover additional players joining the game.
  • Player who initiated the game can decide to stop the polling and start the game; alternatively, the game will automatically be started if 4 players have joined (since that's the maximum it supports).
  • Once the game is to be started, it's set to in_play = true in the database, and the flashvars string is sent to the AS (including: game_id, track_num, pid, numPlayers...yeah for variable-name inconsistency!)
Multi-game support:
  • Can start a new game from the index page - game_id is sent through the querystring as -1 to indicate a new game should be started; PHP figures out the current highest game_id number and inserts a new game to the db with the next-highest id number.
  • Index page displays a list of games that have been initiated but are not yet in play (i.e. are open for more players to join) - these have not yet been closed by their organizer and still have fewer than 4 players.  One need simply click to join; game_id is passed in the querystring and retrieved in the PHP via the global $_GET variable.
  • The game_id is sent to AS through flashvars as mentioned above.
Multi-track support:
  • I made a MovieClip object called 'tracks'.  Each of 4 keyframes will eventually display a different track I've drawn, but right now they all show the same one.
  • When you are starting a new game from the index page, thumbnails of the 4 options for default tracks are shown, and you simply select one before you click the 'go' button to head to the playgame.php page.   The track number you've selected is submitted via a form POST.
  • For new games, the playgame.php page pulls the selected track number from the form POST and inserts it as one of the fields when adding the new game to the database.
  • For existing games (that you're joining), the page queries the database based on the game_id provided in the querystring and thereby pulls the track_num.
  • The track number is then passed to the AS as one of the flashvars.  Since this number refers to the keyframe of the instance of the 'tracks' MovieClip, I simply pass it as a parameter to display the correct track: realtrack.gotoAndStop(track_num).
  • NOTE: I've currently not implemented support for players drawing their own tracks... If I get to that, I'm not sure if I will be able to associate the track number to each game by just storing it as one of the attributes in the Games table.  My original database design includes a separate table for tracks, and I imagine storing custom tracks somehow would require this table, BUT, right now, with only my 4 possible default tracks it works just fine to have an id number (1 - 4) as an attribute for each game.
Well this post feels a bit disorganized and haphazard... I will try to post a screen-capture video of all this stuff soon.

Thursday, April 15, 2010

Non-retangular tracks will work after all

Dear Joe: I don't think I'm capable of writing a short post.

So you know how in the past, the "track" has always been two concentric rectangles?  That's because I was simply drawing it as a Shape object.  To be able to call hitTestPoint on it (to check whether players' pieces were inside the track), I added a fill to the track space, but set alpha = 0 for those pixels so they would be transparent but still register as hits.

Well, no one wants to play this game on a rectangular track all the time.  So it's finally time to make more complex tracks work.  I didn't want to try to draw a curving track by piecing together arcs using the drawing API, so instead I made a new MovieClip object to add to the library.  I called it 'tracks' and am thinking I'll make several tracks, each one on a keyframe.  The user will somehow be able to select one, and then I'll just call gotoAndStop(frame#) on the one instance of the tracks object, and add it to the main scene.

For now I just drew one track using the pen tool and smoothing out the corner joints (vertices) between line segments.  If you go into the menu (Modify -> Shape -> Advanced Smooth...) you find a function that'll create curvature-editing handles for the vertices.  It's not exactly what I was expecting based on experience with Illustrator, but it came out decent enough.  Luckily, I was able to add a transparent fill within the track area (just using the fill tool), just like I'd done programmatically with the rectangle track.  And the bounds testing using hitTestPoint works = one hurdle easily cleared.

[Next steps: multiple tracks to choose from; users drawing their own tracks...!]

Resizable iframe

As I mentioned in my post about setting up the Facebook app, I selected the option to allow the iframe (which holds all of my content -- it pulls my app pages from toryg.net) to be resizable.  I didn't really know what that meant when I was setting it up, but now I've realized: if my content is longer vertically on the page than what's seen in the browser window, the content just gets cut off.  (Thanks to Firebug, I found the css command that was setting the iframe to have overflow: scroll; -- so when I was testing two instances of the swf on one page, I would simply go into Firebug and cancel that line so I was able to scroll the iframe and see everything.)

Today I decided to figure out how to actually fix this.  Thanks to the resizable iframe page (on the Developers' Wiki), I found out I had to set up two things first:
Then in JavaScript I had merely to call up the CanvasUtil features, set the location of the xd_receiver.htm file, and call a library function (startTimerToSizeToContent()) which watches changes in the canvas content and automatically resizes the iframe appropriately.  (In other words, I copied the several lines of code from the resizable iframe page.)

The only hitch I had was that at first I was placing those few lines of Javascript up in the head of the page, and then that hidden div in the body hadn't been created yet when the script was trying to initialize.  Thus, unfortunately, I have to stick the script in the body, right after that div.  But it works now, so that no matter how vertically-long my content is, the iframe changes to accommodate it and I can scroll freely and see everything.

Tuesday, April 6, 2010

Quick note on the database engine

I met up with Jeff Nimeroff briefly because I thought I was about ready to set up the actual database tables and start using those in place of the test tables with which I'd been working.  I'd forgotten which engine he had suggested to use, and why, from a previous discussion.

Though there are a number of engines that MySQL supports, Jeff explained that the two main engines used are MyISAM and InnoDB.  The key difference between them is that MyISAM locks entire tables during queries, whereas InnoDB only locks at the level of a single row.  The functions of my game require running various polling functions that query the database so that players check for and receive updates on each others' actions.  Therefore it probably does not make sense to lock entire tables when, for example, up to 4 client instances of the game are making calls to the same table at once.  Thus he suggested I go with the InnoDB engine.

However, because this is a turn-based game, for the most part only one client can be making an update or insertion query at a time.  Therefore it's not totally critical to avoid table-locking.

Finally, he informed me that I can switch the database engine at any time--I'd thought it was set in stone, which is why I'd wanted to check in with him about which to use prior to creating the "real" database tables.

Monday, April 5, 2010

Multiplayer WORKS! (separate instances updating through the database)

Joe gave me permission to make the blog posts short and sweet.  I'm going to try to keep this one more at an overview level.

The goal I set out to accomplish was to separately run multiple instances (i.e. one per player) of the swf with each one keeping itself updated on the actions made (turns taken) by the others.

How it works:
  • In the HTML for the embed/object elements, the FlashVars parameter provides (a) the total number of players in the game, and (b) with which player number that particular swf instance is to be associated.
  • As the "whole document" class (TraceRace) is a DisplayObject, its Actionscript constructor uses its LoaderInfo instance to grab the values of the FlashVars.
  • When it's a player's turn, that swf will both show and allow clicking on the options for the next turn.  Upon the click of an option, the URLVariables/URLLoader/etc. classes send off this data to a PHP handler script, which sends a MySQL query to input a row in the database table.
  • When it's not a player's turn, that swf will show the other player's turn options, but they are not clickable (so that you can't go taking someone else's turn).  The swf uses the same URL[whatever] classes to poll for other players' turns showing up in the database: every two seconds, a POST is made which is handled by a PHP script.  If the script returns the other player's turn data (since it has been entered in the database), it stops polling, and this swf updates the other player's move visually and in its own state (variables).  If the other player's turn has not been entered into the database yet, the script just returns a boolean indicating such, and it initiates the next polling request.
Problems:
  • After I thought I'd figured them out last time, turned out I still was having issues with booleans.   I'm now sending booleans through PHP echoes as 1 (true) or 0 (false), because the AS can easily convert them to actual booleans, whereas the word strings "true" and "false" are not so easily converted.
  • During debugging I ended up trying to be pretty precise with types, even though I'm not always sure if that was causing a bug or not.  For example, I cast variables parsed from PHP-echoed name/value strings to, say, a Number or a uint.  I also cast a piece to a piece when I pull it out of an array, since I don't think arrays are designated to hold a certain single type.
  • It looks like AS isn't happy with my passing an argument as 'null' (since I was going to retrieve the necessary object a different way, once inside the function).  Then I tried using two parameters with default values, but since you can only leave out arguments at the end of the list, I can't input the 2nd and drop the 3rd in one call while doing the reverse in another call.  Eventually I just wrote a getter function so I could access the object before the problematic call.
  • One of the weirdest problems I've had yet, I actually managed to create myself.  By initializing an Array with a specific length, and subsequently pushing more elements into it, I ended up with empty spaces prior to the pushed elements.  This actually makes pretty decent sense.  But since previously I hadn't initialized the array with a length, the pushed elements just added/filled spots from the beginning.  I finally realized what was going on and now add the elements by accessing the array slots via indices.
Now for the cool part.  I took a screencast video (thanks Screencast-O-Matic!!!) to show how this actually all operates in my testing setup.


Wednesday, March 31, 2010

Successful push of variables in AS =[PHP]=> database, plus AS callback

So the other night I set out to implement the plan I outlined before to figure out how to push data (i.e. each turn's information) from Actionscript variables to the database.  Here are the components I wrote/set up:
  • Database:  set up a test table called TestTurns with 3 fields - p_id (piece's ID number), xpos, and ypos.
  • Actionscript:  added a storeTurn() method to the 'piece' class, which makes use of the URLVariables, URLRequest, and URLLoader classes to structure and send an HTTP POST request, containing a piece's ID number, x-position, and y-position, to the PHP file.  Also wrote a callback function - which gets attached to the Loader's COMPLETE event - that spits the echoed data from the PHP script (provided in name/value query-string format) into a URLVariables object and "logs" some information into a dynamic text box currently sitting in the middle of the race track.  (This is a temporary debugging mechanism, obviously.)
  • PHP:  wrote a small script in a file which grabs the 3 POSTed variables, runs an insert query on the database, and echoes back the information it received and whether the query was successful.
These components seemed straightforward enough, but I did run into some issues along the way.
  • First, I was trying to test the POST to the PHP script by running the Actionscript locally.  It was able to pull the file, but in the AS callback function I was getting these strange strings as supposed value counterparts of the names that the URLVariables constructor was able to parse out.  I rearranged and tested things for some time until the "well duh" moment: I was expecting PHP to run while sitting on my machine.  Oh, wait.  :)  (To test locally, I wrote a simple text file containing a name/value string, directed the HTTP request to that file, and the URLVariables constructor parsed it just fine.)  After this, I uploaded the files so I could test it for real (i.e. with the PHP running).
  • I knew that for an insert query, the mysql_query function would return a true or false based on whether the query was successful or an error occurred.  However, I didn't know the details on the representation of that boolean, and how it would get converted when echoed by the PHP and then parsed by the URLVariables constructor in the Actionscript.  From the output I was getting dumped into the "log" it was appearing as a 1 or 0 instead of the string "true" or "false", so in order that the URLVariables variable for 'success' would actually have a value of true or false (that could then be used in a conditional), I just used the ternary conditional operator (?:) to convert the 1/0 value ahead of time in the PHP echo string.
  • Also from the output log I realized that since I was calling (in the handler PHP file) require_once for the db_connect file, whatever was echoed in that file was showing up amidst what the handler file was echoing.  Naturally this was screwing up the name/value query string I was trying to create so that the URLVariables constructor (in AS) could read it.  I was looking through the PHP documentation for the include/require functions to see if there was a way to suppress the output of the included file, but didn't find anything along those lines.  As an alternative for echoing messages of success or error in db_connect.php, I created a variable to store a boolean for connection success.  Then, when I require db_connect.php in the handler file, I can store that boolean (i.e. $connect_success = require_once "db_connect.php";) to discover and deal with connection errors.
Once I had figured out these problems, though, it actually worked...!  I took screenshots both of the turn data in the test table and the output as it showed up in Flash:

Tuesday, March 23, 2010

Storing a Turn: From Actionscript to the Database (Plan)

After deciding last night might be a good time to do something useful for my project, I chose to tackle an important feature component of my game that I had yet to really look into: making it multiplayer across client machines.  In other words, I needed to figure out how to:
  • take the occurrence of one player's turn (which happens on the Flash movie on one player's browser), 
  • send the critical information (which player and their new position on the grid) to the database on the server, 
  • and update the display of that player on all of the other players' Flash movies.
Since PHP is my avenue to the database, I set out to learn how to send information between Actionscript and PHP.  I stumbled on a forum thread that includes enough code to seemingly walk me through the necessary steps for packaging up Actionscript variables and sending an HTTP POST request to a PHP script (which can then insert values into the database).

In addition, the thread mentions that you can attach a callback function which will handle the event of the completion of the request--i.e. once the data has been loaded from the requested URL, that function is called and you can access the returned data.  So in the PHP I'll check on the success of the database insertion and send that result back (which I believe is just via 'echo' in PHP... wow I'm rusty on PHP).

I intended to draw a higher-level diagram of how this would all work, but I ended up writing in what is almost entirely correct code (as opposed to pseudocode or less).  I have some explanatory side notes written in, and arrows to show the overall informational flow.


*Important note*  I realized that this plan encompasses only the communication between one instance of the Flash game (on one client browser) and the database.  It does not cover the implementation of updating multiple players' instances of the same game on different clients, but it is the first step towards that.


Anyway here's the diagram.  (As usual, click to see in full size.)  I have a propensity for hand-drawing/writing stuff these days, it seems.

Friday, March 19, 2010

Found my game on BoardGameGeek!!

Joe suggested I try to find and scan an old piece of paper on which my dad and I had played the game.  I'll probably try to do that, but in the meantime, I found a listing for the game under the name "Racetrack" on boardgamegeek.com.  There are also some user-submitted photos.  My favorites...

(click the images to see them in full size)

Love the comments... The caption on the page says this one is from the 1960s!

Thursday, March 18, 2010

Meeting with Dr. Nimeroff: Database structure and linking multiple players

I met with my adviser, Dr. Nimeroff, for a bit the other day and we mainly discussed two related points.  Since it's been awhile since I've tried to design a database, we first talked over my ER diagram.  He didn't see any major potential issues with it right off the bat, so that was a good start.  We focused on the 'player_group' relation between the 'games' and 'users' entities.  I had not been sure whether it would be necessary to store player state information, like the x- and y-position, for a running game--in my newness to Flash/Actionscript, I'd been thinking something like that could be stored in Actionscript variables.  Dr. Nimeroff explained, though, that the Flash swf and associated Actionscript are run locally on each user's browser, so when values and visual elements change for one player, they aren't automatically updated for other players.  The key here is that whatever information is needed to update players' states on each others' "copies" of the swf and of variable values in the code must be saved into the database (since that, of course, is stored on the server) and then retrieved by calls from the other players' browsers to receive the new state.  Thus, it was a good thing I included such state information in my ER design.

Along these lines, I then asked about an issue that has been a "hole" in my mental plan of implementation since the beginning: how do other players' local machines find out that an update has been made to the database and take appropriate action to update their own copy of the game state in the code and swf?  I do want the game to be real-time, though it is turn-based, which makes it far easier than having to support multiple players doing things at the same time.  The only thing I could think of was polling; the Actionscript would just ping the database every fixed interval of time to look for a change and pull the new info if necessary.  I had also wondered whether the reverse idea would be feasible: could the database notify the game instances when an update was ready?  Dr. Nimeroff said that the former (games-poll-database) method was definitely the way to go about it.  Since the game is turn-based, it's not creating a constant stream of updates that need to be passed around; thus, a polling request every couple of seconds or so would not be a strain on the server.  Plus, the amount of data to be transferred per request is very little.  Finally, this is much simpler than the database trying to keep track of which instance to notify, where to send the notification, and the like.  This conclusion was very exciting for me, as I am no longer in the dark about a doable and sensible approach the implementation of multiplayer support.  And I think I might even know at a high level how to go about it.  Yay!

Tuesday, March 16, 2010

Discussion with Dad, part 2: Detecting boundaries and out-of-bounds moves

I also spoke in a more high-level sense with my dad about an important issue that I need to solve programmatically via Flash and Actionscript: the program's "awareness" of boundaries.  Here are some methods that I or we came up with:  (Note that I may not have yet researched the feasibility of each.)
  • Store a 2D array of booleans.  Each cell corresponds to a grid point (i.e. potential position) and the boolean simply states whether the point is inside (true) or outside (false) the track boundaries.  This takes initial overhead to create but makes detection of out-of-bounds moves a simple array-access call.  I could create such arrays manually for tracks I draw that will be provided as pre-made tracks; the bigger issue is how to compute this array for user-drawn tracks.  My dad's solution to that is to allow users to draw the track only as a series of connected straight line segments; as each segment is created, the its slope (change in x / change in y, in grid-points) can be used to set the booleans for nearby grid points.  [This corresponds to the left-hand part of the scanned diagram below.]
  • Check each player move for bounds-crossing.  An idea I had already had is for each time a player selects their next move, a line will be "drawn" (programmatically and hopefully invisibly) from the previous position to the new one.  Then, check if this line intersects with (crosses) a track boundary.  I'm not exactly sure how such an intersection check would be accomplished in Actionscript, though I tried to look around a bit for some methods (see the resource links post)...  [This corresponds to the right-hand side of the diagram below.]
  • Use the intrinsic ability of Flash to "know" which area is inside and outside a shape.  This is not as crazy as it sounds.  My dad explained that such data must be stored somehow; the simplest justifying example is that enclosing bounds (i.e. a shape) can be filled with a color.  Since I had no clue how I might access this stored data, I realized maybe I could take the color-filling ability at face value: maybe I can just fill the space outside the outer boundary and inside the inner boundary with a color, and check pixel colors to see if a particular move is in bounds or not.  Again, I figure this can be accomplished with Actionscript; the solution could range from not-so-elegant to rather-hackerish.  Anyway, I'll have to look into it.  [This only corresponds to diagrams in my head, so far.]

Dicussion with Dad, part 1: Handling when a player gets stuck out of bounds

Over lunch with my dad during spring break, I finally remembered to ask him about the one critical point of the game rules that I could not remember: what happens when a player's speed forces them to move out of bounds? -- In other words, all of the 9 possible positions for their current move are located outside the outer edge of the track, or inside the inner edge.  (From playing games with my dad, I definitely remember that this happens sometimes.)

My original idea for handling such a case was to set the player back to their latest prior position (past move) such that subsequent speeds/moves would not force them out of bounds.  As my dad pointed out, this might require backtracking by more than just one previous move, because that might not be enough to allow them to decrease speed within the next move (and stay in bounds).  The process of backtracking and computing at each prior position whether or not the player could stay in bounds from that point would also be a bit of a tedious procedure.  Finally, he maintained that this solution is "unrealistic"; in other words, you don't go backing up your car and slowing down on an actual race track.  Keeping the game realistic isn't especially important to me, but it's kind of a nice feature to attempt to uphold.  (It's kind of like when movies try to stay scientifically accurate.)

My dad's alternative solution is apparently what we always did when we used to play the game (and I just forgot).  It's actually very simple:
  1.  When all of a player's options for their current move are out of bounds, the player simply takes the move anyhow.
  2.  On their next turn, they lose a turn: their piece/marker stays in place on the grid, but they are considered to hold the same speed in the x and y directions.
  3.  On the subsequent turn, they take a move, maintaining the same rules about speed--the x and y speeds may change by 1 unit in either direction.
  4.  This alternation between lost turns and normal-movement turns continues for as long as the player is outside of bounds.
Obviously, the point here is to slow the player down while outside bounds, and hence encourage them to get back in bounds as efficiently as possible.

Here's the diagram my dad sketched to show this.  (Ignore the lines on either side of the '2' and the dots they lead to--that was a different idea.)  Suppose a player is forced to move outside the track on the turn indicated by the line next to '1'.  Their next turn, then, is lost; my dad designates this with the circle surrounding the dot.  The circle means they've stayed at that location for a lost turn.  Two more alterations of turn/loss are then depicted, as the player aims to return to the track.

Just to note, I'm not sure yet how I'll indicate visually that the player has stayed in place for the lost turn, since so far in development the game display is not exactly like it would be on paper in some ways.

Thursday, February 25, 2010

Hook me up

I'm all done with classes for the week, got some time before the evening activities, sitting in the 34th & Walnut Starbucks... goals for the session are: to connect via PHP with the test database I set up on my server space - at the least to pull info from it and spit it out onto the page, if not to insert new records to the one table - and to get a few calls to the Facebook API working via the Facebook PHP client library.

First I'm checking to make sure PHP works in the first place.  I made my test.html page into test.php and threw in a call to phpinfo().  It works, so it looks like I'm good to go.  Now to figure out database access.  I created a new database user and gave the user access to the app database (which currently only has the one test table).  I'm not sure yet exactly what functionality the app will need to have in accessing the database, so I somewhat arbitrarily gave the following permissions for now: select, insert, update, delete, drop, and lock tables.

In search of a refresher on integrating a MySQL database, I came upon this more general tutorial on building various parts of a Facebook app.  (For now I am only referencing the MySQL example section.)  For organization's sake, I created a file called db_connect.php just to encapsulate the connection code.  Following the example, I inputted "localhost" as the host (following this Support Center answer) along with the user and password I just created.  I also looked up the PHP documentation for the mysql_connect function and based on the example there I added a test for whether the connection attempt was successful.  Since no error message appeared, I assume the connection worked.  The last step is selecting the particular database to use.  I added a call to mysql_select_db and a test for success.  This worked as well.


Redux: So I think I might be documenting my every action a little too carefully, because this took more time than it should for only a bit of code.  I didn't make it to actually pulling from or inserting into the database, and nowhere close to trying out Facebook API calls.  But I figured out the right parameters to connect and it worked, so I did make it that far.

Wednesday, February 24, 2010

Setting up the Facebook application

Luckily this task does not seem nearly as daunting to me as it might, since I have worked through the process before when I took the User Interfaces and the Web course (which was CIS 399.006 prior to the renaming of all the 399s) and my partner and I were creating a Facebook app for our project.  However, since things may have changed since then, I've decided to figure out the process again with the help of instructions and documentation, and consult my code from the prior app only if I get stuck.

Some of the basic information was easy: what to name the app (Trace Race), an email address for Facebook to use to contact me, and an email address for app users to contact the developer (which I set to the email I created while setting up my web hosting service).  There are also spaces to fill in a few callback URLs, but as I haven't created any of the pages yet, I'm leaving those blank for a moment.

The first major choice to be made was whether to build the app as an iframe or with FBML.  I found an article on the developers' wiki explaining the differences and pros/cons of both structures.  After reading this over, I decided to go with the iframe-based structure for a few reasons: it reduces the overhead for AJAX calls (which I do expect to use), it avoids the slowdown caused by reloading Facebook Chat (this happens in FBML apps because page reloads must reload the whole page, whereas iframe reloads only reload the frame), and XFBML provides a faster way to use Facebook elements or access Facebook data than making individual API calls (and I don't expect to be making many of those anyhow).

The next question was whether to use the resizable iframe option.  This allows some control over the size of the iframe as opposed to having to fit the app into a fixed-size frame.  Although I am not sure yet how large the Flash gameboard area will be, I imagine that the pages without the gameboard and the pages with the board will probably have a different-sized content area, so I will try out turning on the resizing option.  Maybe I will turn it off later once I see how the pages are coming together.

Finally (though the app isn't totally set up--have to connect the database and hook in Facebook API code), I ran the simplest of tests.  On my web hosting space I created a directory for the pages for this app and entered it as the canvas callback URL in the app settings.  I chose a canvas URL, which is the direct link to the app in the form of "http://apps.facebook.com/[name]": http://apps.facebook.com/tracerace/.  So now if you access the canvas URL, you'll see the directory index (being pulled via an iframe from my server space) and can click to the test.html page.  Sweet!

p.s. I have added a screenshot showing the test page on my server space being pulled into the app's iframe, BUT, since the app is indeed live and publicly accessible--albeit in a primitive bare-bones fashion--you can actually go to the URL and see it yourself.  (At least, I believe so--a Facebook account may be required.  I haven't tested that.)