# Create a User-Profile Mini-Game [closed]

Yesterday, I stumbled on a very clever thing.

Yes, that's a working implementation of Tic-Tac-Toe on a user profile page, from @minitech. Of course, the moment I saw it, I had to reverse engineer his idea and one-up him :P

Here's my own example embedded directly in the post. It's a tad buggy due to a couple of implementation details I haven't worked out a good solution for. Sometimes after you click a peg, it doesn't update properly until another page refresh:

## Can you do better?

• Create a working game in your posted answer (or your user profile page). This is done via appropriately configuring a webserver you own (or writing a program that acts as a webserver), and embedding content from it in a post, using the referer to determine what commands the user is giving to the game.
• Coolest idea (most votes) wins the competition, by Canada Day (Sunday, July 1, 2012 @ 11:59 PM EST)
• In the event of a tie, the older answer wins.
• +1 Simple, but brilliant idea! BTW - for the deadline I think you mean June 2, 2012. – Cristian Lupascu May 23 '12 at 15:22
• Derp, yes I did, thanks :) – mellamokb May 23 '12 at 15:23
• @boothby: I was actually thinking about deleting my answer. The intention was to provide a concrete example, not to win the contest (or votes, I don't greatly care about rep). Can you provide some constructive suggestions to the competition? What would you like the deadline to be? How should the spec be changed to motivate you to participate? – mellamokb May 23 '12 at 19:02
• I just noticed that minitech's AI can't play a perfect game of tic-tac-toe. Play center, bottom-left, top-center, center-right, center-left. – PhiNotPi May 23 '12 at 23:04
• @Mr.Wizard: working fine on FF 12.0 & Windows 7 here, could you post more details about what's not working? – ChristopheD Jun 4 '12 at 13:36

## Conway's Game of Life

Load pattern: random - glider - gunstar - snail - lwss - lightspeedoscillator1 - tumbler

Used Python and SVG output. I have tried using single pixels at first (so you could toggle single cells), but it did not work out, because the browser does not load images in order. Also, much bigger patterns are possible like this without crashing my webserver.

Update:

I had some fun with python and added several features and improvements:

• Added HUD with population count, zoom and name
• Patterns in the rle format can now be loaded (long list, via) using the pattern parameter (e.g. ?pattern=glider). The filesize is limited to 1.5kB
• Can forward n generations, limited to 5 at a time, using the next parameter
• Slightly improved algorithm. It's not really fast though, I want this to stay simple
• It also works standalone now (uses either referer or its own query string): https://copy.sh/fcgi-bin/life2.py?pattern=gosperglidergun

sessions = {}

WIDTH = 130
HEIGHT = 130
RULE = (3,), (2, 3)

filename = PATH + filename + '.rle.gz'

try:
if os.stat(filename).st_size > 1500:
return ['pattern too big', set()]
except OSError as e:
return ['could not find pattern', set()]

file = gzip.open(filename)

x, y = offset_x, offset_y
name = ''
pattern_string = ''
field = []

for line in file:
if line[0:2] == '#N':
name = line[2:-1]
elif line[0] != '#' and line[0] != 'x':
pattern_string += line[:-1]

for count, chr in re.findall('(\d*)(b|o|\$|!)', pattern_string): count = int(count) if count else 1 if chr == 'o': for i in range(x, x + count): field.append( (i, y) ) x += count elif chr == 'b': x += count elif chr == '$':
y += count
x = offset_x
elif chr == '!':
break

file.close()

return [name, set(field)]

def next_generation(field, n):

for _ in range(n):

map = {}

for (x, y) in field:
for (i, j) in ( (x-1, y-1), (x, y-1), (x+1, y-1), (x-1, y), (x+1, y), (x-1, y+1), (x, y+1), (x+1, y+1) ):
map[i, j] = map[i, j] + 1 if (i, j) in map else 1

field = [
(x, y)
for x in range(0, WIDTH)
for y in range(0, HEIGHT)
if (x, y) in map
if ( (map[x, y] in RULE[1]) if (x, y) in field else (map[x, y] in RULE[0]) )
]

return field

def life(env, start):

else:
client_ip = '0'

if not client_ip in sessions:
sessions[client_ip] = read_pattern('trueperiod22gun', 10, 10) + [2]

session = sessions[client_ip]

if 'HTTP_REFERER' in env:
query = urlparse.parse_qs(urlparse.urlparse(env['HTTP_REFERER']).query, True)
elif 'QUERY_STRING' in env:
query = urlparse.parse_qs(env['QUERY_STRING'], True)
else:
query = None

timing = time.time()

if query:
if 'next' in query:
try:
count = min(5, int(query['next'][0]))
except ValueError as e:
count = 1
session[1] = set( next_generation(session[1], count) )
elif 'random' in query:
session[0:2] = 'random', set([ (random.randint(0, WIDTH), random.randint(0, HEIGHT)) for _ in range(800) ])
elif 'pattern' in query:
filename = query['pattern'][0]
if filename.isalnum():
elif 'zoomin' in query:
session[2] += 1
elif 'zoomout' in query and session[2] > 1:
session[2] -= 1

timing = time.time() - timing

start('200 Here you go', [
('Content-Type', 'image/svg+xml'),
('Cache-Control', 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'),
('Expires', 'Tue, 01 Jan 2000 12:12:12 GMT')
])

pattern_name, field, zoom = session

yield '<?xml version="1.0" encoding="UTF-8"?>'
yield '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
yield '<!-- finished in %f -->' % timing
yield '<text x="0" y="10" style="font-size:10px">Population: %d</text>' % len(field)
yield '<text x="100" y="10" style="font-size:10px">Zoom: %d</text>' % zoom
yield '<text x="180" y="10" style="font-size:10px; font-weight:700">%s</text>' % pattern_name
yield '<line x1="0" y1="15" x2="666" y2="15" style="stroke:#000; stroke-width:1px" />'

for (x, y) in field:
yield '<rect x="%d" y="%d" width="%d" height="%d"/>' % (zoom * x, zoom * y + 20, zoom, zoom)

yield '</svg>'

from flup.server.fcgi import WSGIServer
import random
import re
import gzip
import os
import urlparse
import time

WSGIServer(life).run()


You can take my code as a template for further python fastcgi submissions.

• +1 Awesome! One suggestion: append #5946 to your links, and it will jump back to your post after every update. – mellamokb May 23 '12 at 20:11
• hmm.. at least it worked when i tried it.. ah. because in Towers of Hanoi you're always clicking on different pegs. hmm – mellamokb May 23 '12 at 20:12
• @mellamokb it works, but you can't click the same link twice now – copy May 23 '12 at 20:13
• Ya, I just realized that lol. Guess you can provide a disclaimer that when doing next generation, just press F5 for future iterations instead of clicking the next link again and again after the first time. – mellamokb May 23 '12 at 20:13
• @mellamokb thanks. In my opinion you don't really need to accept answers on this platform, because it looks as if the challenge is closed – copy Jul 2 '12 at 19:09

# C# - Stack Exchange Hangman

Guess names of Stack Exchange websites in this Hangman game:

This was done using ASP.NET MVC 3.0. Here's the code of the Controller that does the trick:

public class HangmanController : Controller
{
public ActionResult Index()
{
var game = Session["hangman"] as HangmanGame ?? HangmanGame.New();

game = ExecuteGameCommand(game);

Session["hangman"] = game;

var imageRenderer = new HangmanImageRenderer(game);
return new ImageResult(imageRenderer.Render());
}

private HangmanGame ExecuteGameCommand(HangmanGame game)
{
var referrerQuery = Request.UrlReferrer != null ? Request.UrlReferrer.Query : string.Empty;

if (referrerQuery.Contains("_new_hangman_"))
return HangmanGame.New();

if(game.IsOver())
return game;

var chosenLetter = HangmanGame.ValidLetters
.FirstOrDefault(letter => referrerQuery.Contains(String.Format("_hangman_{0}_", letter)));

if (chosenLetter != default(char))
game.RegisterGuess(chosenLetter);

return game;
}
}


Other than this code, there are three more classes that I haven't included since they are pretty long and straightforward:

• HangmanGame - here's where the game business rules are implemented
• HangmanImageRenderer - the class that encapsulates all the GDI ugliness
• ImageResult - a custom ActionResult that is used to return a dynamically generated image

The entire code listing is available at http://pastebin.com/ccwZLknX

• +1 Wow, you guys are awesome :). Like the ideas so far! – mellamokb May 24 '12 at 20:24
• Cool, never heard of appharbor.com. Are you really paying to host your answer? – mellamokb May 24 '12 at 20:25
• @mellamokb no, I'm using the Appharbor's free hosting plan. If this gets clicked a lot I guess I'll have to pay though... :) – Cristian Lupascu May 24 '12 at 20:29
• I was going to mention if necessary, I can provide custom subdomain hosting and FTP access to my hosting site. – mellamokb May 24 '12 at 20:32
• @mellamokb thanks, but I think this hosting will do. I was just joking about many clicks. :) – Cristian Lupascu May 25 '12 at 6:34

# Clojoban! [WIP]

I wanted to make a bigger game out of this to learn Clojure, so this took a while to pull off (and got pretty big.) I've had a lot of fun doing it, btw!

. .

. .

## Instructions

You are Robby , a hard-working robot. You work at a FlipCo Industries as a heavy load carrier. Your job is to move each box to a goal spending as few steps as possible. FlipCo's facilities are DANGEROUS. There are lots of challenges and special places to discover.

If you get stuck, click Restart level (but your step count won't be reset!)

You can also play at Clojoban's front page (although it kind of ruins the purpose of the challenge.) It fixes the infamous anchor problem, doesn't require cross-site cookies and you can play with your keyboard arrow keys! You can also play at my user profile page without the annoying anchor problem.

In Firefox the image doesn't flicker while it's loading so it's a bit more comfortable to play.

This game is FAR from completion, Clojoban is still a work in progress. You can see the complete sourcecode at Clojoban's GitHub project page. There's some info in the README about contributing. I need levels too! See the level format at the example levels. You can peek at Clojoban's issue tracker and see what's coming next!

• Now your reputation is 11 :) – mellamokb Jun 16 '12 at 14:04
• @mellamokb thanks! Game is embedded now :) – Álvaro Cuesta Jun 16 '12 at 16:23
• Apparently this didn't get much attention. Any tips for improving? – Álvaro Cuesta Jun 21 '12 at 15:41
• Your answer is good, I think this question overall has stagnated. I haven't seen much activity or votes for the last couple of days. – mellamokb Jun 21 '12 at 17:27
• That's a great game! I think you should make a standalone version; I got to the third level and got rid of pressing the No-op button. :) Anyway, great work! – Cristian Lupascu Jun 23 '12 at 10:48

## Maze

(source: t15.org)

New - Noop button

I started from the PHP maze generator I found here: http://dev.horemag.net/2008/03/01/php-maze-generation-class/.

EDIT: changed the output to PNG instead of SVG (for better cross browser compatibility).

EDIT 2: added a header for fixing IE cookie compatibility. Should now work correctly in all major browsers.

The image does not refresh if you take the same direction twice (due to the anchor links). Press F5 the second time, or play the maze on my stackoverflow profile.

EDIT 3: Added a no-op button for easily being able to take the same direction twice (see comments below).

<?php
// based upon the maze generator by Evgeni Vasilev (PHP Adaptation)
// see http://dev.horemag.net/2008/03/01/php-maze-generation-class/
class Maze
{
var $maze = array(); var$mx = 0;
var $my = 0; var$xplayer = 1;
var $yplayer = 1; function Maze($mx, $my) {$mx +=2;
$my +=2;$this->mx = $mx;$this->my = $my;$dx = array( 0, 0, -1, 1 );
$dy = array( -1, 1, 0, 0 );$todo = array();
$todonum = 0; for ($x = 0; $x <$mx; ++$x){ for ($y = 0; $y <$my; ++$y){ if ($x == 0 || $x ==$mx-1 || $y == 0 ||$y == $my-1) {$this->maze[$x][$y] = 32;
} else {
$this->maze[$x][$y] = 63; } } }$x = rand(1, $mx-2);$y = rand(1, $my-2);$x = 1; $y = 1;$this->maze[$x][$y] &= ~48;
for ($d = 0;$d < 4; ++$d){ if (($this->maze[$x +$dx[$d]][$y + $dy[$d]] & 16) != 0) {
$todo[$todonum++] = (($x +$dx[$d]) << 16) | ($y + $dy[$d]);
$this->maze[$x + $dx[$d]][$y +$dy[$d]] &= ~16; } } while ($todonum > 0) {
$n = rand(0,$todonum-1);
$x =$todo[$n] >> 16;$y = $todo[$n] & 65535;
$todo[$n] = $todo[--$todonum];
do {
$d = rand(0, 3); } while (($this->maze[$x +$dx[$d]][$y + $dy[$d]] & 32) != 0);
$this->maze[$x][$y] &= ~((1 <<$d) | 32);
$this->maze[$x + $dx[$d]][$y +$dy[$d]] &= ~(1 << ($d ^ 1));
for ($d = 0;$d < 4; ++$d){ if (($this->maze[$x +$dx[$d]][$y + $dy[$d]] & 16) != 0) {
$todo[$todonum++] = (($x +$dx[$d]) << 16) | ($y + $dy[$d]);
$this->maze[$x + $dx[$d]][$y +$dy[$d]] &= ~16; } } }$this->maze[1][1] &= ~1;
$this->maze[$mx-2][$my-2] &= ~2; } function _drawLine($img,$color,$x1, $y1,$x2, $y2) { imageline($img, $x1,$y1, $x2,$y2, $color); } function _drawPlayer($img, $x,$y, $r,$colorborder, $colorfill) { imagefilledellipse($img, $x,$y, $r,$r, $colorfill); imageellipse($img, $x,$y, $r,$r, $colorborder); } function _drawWin($img, $color) { imagestring($img, 5, 170, 90, "YOU WIN!", $color); } function movePlayerDown() { if ($this->yplayer+1 < $this->my-1 && ($this->maze[$this->xplayer][$this->yplayer] & 2) == 0)
$this->yplayer++; } function movePlayerUp() { if ($this->yplayer-1 > 0 && ($this->maze[$this->xplayer][$this->yplayer] & 1) == 0)$this->yplayer--;
}

function movePlayerRight()
{
if ($this->xplayer+1 <$this->mx-1 && ($this->maze[$this->xplayer][$this->yplayer] & 8) == 0)$this->xplayer++;
}

function movePlayerLeft()
{
if ($this->xplayer-1 > 0 && ($this->maze[$this->xplayer][$this->yplayer] & 4) == 0)
$this->xplayer--; } function renderImage($xs, $ys) {$off = 0;
$w = ($this->mx*$xs)+($off*2); $h = ($this->my*$ys)+($off*2);
$img = imagecreatetruecolor($w, $h); imagesetthickness($img, 2);
$fg = imagecolorallocate($img, 0, 0, 0);
$bg = imagecolorallocate($img, 248, 248, 248);
$red = imagecolorallocate($img, 255, 0, 0);
imagefill($img, 0, 0,$bg);
if (($this->xplayer ==$this->mx-2) && ($this->yplayer ==$this->my-2)) {
$this->_drawWin($img, $red); return$img;
}

for ($y = 1;$y < $this->my-1; ++$y) {
for ($x = 1;$x < $this->mx-1; ++$x){
if (($this->maze[$x][$y] & 1) != 0)$this->_drawLine ($img,$fg, $x *$xs + $off,$y * $ys +$off, $x *$xs + $xs +$off, $y *$ys + $off); if (($this->maze[$x][$y] & 2) != 0)
$this->_drawLine ($img, $fg,$x * $xs +$off, $y *$ys + $ys +$off, $x *$xs + $xs +$off, $y *$ys + $ys +$off);
if (($this->maze[$x][$y] & 4) != 0)$this->_drawLine ($img,$fg, $x *$xs + $off,$y * $ys +$off, $x *$xs + $off,$y * $ys +$ys + $off); if (($this->maze[$x][$y] & 8) != 0)
$this->_drawLine ($img, $fg,$x * $xs +$xs + $off,$y * $ys +$off, $x *$xs + $xs +$off, $y *$ys + $ys +$off);
if ($x ==$this->xplayer && $y ==$this->yplayer) {
$this->_drawPlayer ($img, $x *$xs + ($xs/2),$y * $ys + ($ys/2), 14, $fg,$red);
}
}
}
return $img; } } header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"'); session_start();$orig_url = $_SERVER['HTTP_REFERER']; if (!isset($_SESSION['maze']) || strpos($orig_url, 'resetmaze')){$_SESSION['maze'] = new Maze(25,10);
}
$maze =$_SESSION['maze'];
if (strpos($orig_url, 'playerdown')) {$maze->movePlayerDown(); }
if (strpos($orig_url, 'playerup')) {$maze->movePlayerUp(); }
if (strpos($orig_url, 'playerright')) {$maze->movePlayerRight(); }
if (strpos($orig_url, 'playerleft')) {$maze->movePlayerLeft(); }
$img =$maze->renderImage(16,16);
imagepng($img); imagedestroy($img);
?>

• +1 Nice! for a better experience, add #answer-6171 at the end of your links. Otherwise, no one will have enough patience to solve the maze. – Cristian Lupascu Jun 2 '12 at 20:56
• @W0lf: Thanks. I thought about including the # links but the problem is they don't refresh the page when you take the same direction twice (which can happen in a maze ;-). I've added them now so people will have to press F5 the second time they want to take the same direction. Another option is to play it here (my SO profile: stackoverflow.com/users/81179/christophed) – ChristopheD Jun 2 '12 at 21:16
• I would fancy a simple no-operation link (refresh?) to make the update easier when trying to move twice in the same direction :) – kaoD Jun 4 '12 at 17:04
• @kaoD: Without the anchor parts (#) which jump to the right answer to the question (internally, without page refresh), a simple page refresh would work fine (as you can see on my linked profile where the same maze is also available). But the problem then would be that you would find yourself at the top of the page after every refresh. The real problem is that we're really limited in what we can include in an answer here on StackOverflow (for good reason off course), we can't use arbitrary Javascript for example. I have no idea about an easy way out. – ChristopheD Jun 4 '12 at 17:32
• You can still have the anchor and it will go straight to your post, but with a different URL (which will allow for correct gameplay.) I find the F5 method clunky. – kaoD Jun 4 '12 at 17:50

## 2-Player Pokémon Chess [Work in progress]

Because it's more fun this way. Coming up some day: AI, isometric grid, and shadows!

http://minite.ch/chess/?i=1http://minite.ch/chess/?i=2http://minite.ch/chess/?i=3http://minite.ch/chess/?i=4http://minite.ch/chess/?i=5http://minite.ch/chess/?i=6http://minite.ch/chess/?i=7http://minite.ch/chess/?i=8
http://minite.ch/chess/?i=9http://minite.ch/chess/?i=10http://minite.ch/chess/?i=11http://minite.ch/chess/?i=12http://minite.ch/chess/?i=13http://minite.ch/chess/?i=14http://minite.ch/chess/?i=15http://minite.ch/chess/?i=16
http://minite.ch/chess/?i=17http://minite.ch/chess/?i=18http://minite.ch/chess/?i=19http://minite.ch/chess/?i=20http://minite.ch/chess/?i=21http://minite.ch/chess/?i=22http://minite.ch/chess/?i=23http://minite.ch/chess/?i=24
http://minite.ch/chess/?i=25http://minite.ch/chess/?i=26http://minite.ch/chess/?i=27http://minite.ch/chess/?i=28http://minite.ch/chess/?i=29http://minite.ch/chess/?i=30http://minite.ch/chess/?i=31http://minite.ch/chess/?i=32
http://minite.ch/chess/?i=33http://minite.ch/chess/?i=34http://minite.ch/chess/?i=35http://minite.ch/chess/?i=36http://minite.ch/chess/?i=37http://minite.ch/chess/?i=38http://minite.ch/chess/?i=39http://minite.ch/chess/?i=40
http://minite.ch/chess/?i=41http://minite.ch/chess/?i=42http://minite.ch/chess/?i=43http://minite.ch/chess/?i=44http://minite.ch/chess/?i=45http://minite.ch/chess/?i=46http://minite.ch/chess/?i=47http://minite.ch/chess/?i=48
http://minite.ch/chess/?i=49http://minite.ch/chess/?i=50http://minite.ch/chess/?i=51http://minite.ch/chess/?i=52http://minite.ch/chess/?i=53http://minite.ch/chess/?i=54http://minite.ch/chess/?i=55http://minite.ch/chess/?i=56
http://minite.ch/chess/?i=57http://minite.ch/chess/?i=58http://minite.ch/chess/?i=59http://minite.ch/chess/?i=60http://minite.ch/chess/?i=61http://minite.ch/chess/?i=62http://minite.ch/chess/?i=63http://minite.ch/chess/?i=64


No en passant or castling, sorry. Checkmate/check/stalemate detection to be implemented. Sprites from here: http://floatzel.net/pokemon/black-white/sprites/

Here's the source:

<?php
session_start();

function kick() {
die('Go away.');
}

function isEnemy($item) { return$item !== -1 && $item & 8; } function iValidMoves($board, $type,$x, $y) {$results = array();

switch($type) { case 0: # Pawn if($board[$y - 1][$x] === -1) {
$results[] = array($x, $y - 1); if($y == 6 && $board[$y - 2][$x] === -1)$results[] = array($x,$y - 2);
}

if($x > 0 && isEnemy($board[$y - 1][$x - 1])) $results[] = array($x - 1, $y - 1); if($x < 7 && isEnemy($board[$y - 1][$x + 1]))$results[] = array($x + 1,$y - 1);

break;
case 1:
# King
if($x > 0 &&$board[$y][$x - 1] & 8) $results[] = array($x - 1, $y); if($x > 0 && $y > 0 &&$board[$y - 1][$x - 1] & 8) $results[] = array($x - 1, $y - 1); if($x > 0 && $y < 7 &&$board[$y + 1][$x - 1] & 8) $results[] = array($x - 1, $y + 1); if($x < 7 && $board[$y][$x + 1] & 8)$results[] = array($x + 1,$y);
if($x < 7 &&$y > 0 && $board[$y - 1][$x + 1] & 8)$results[] = array($x + 1,$y - 1);
if($x < 7 &&$y < 7 && $board[$y + 1][$x + 1] & 8)$results[] = array($x + 1,$y + 1);
if($y > 0 &&$board[$y - 1][$x] & 8) $results[] = array($x, $y - 1); if($y < 7 && $board[$y + 1][$x] & 8)$results[] = array($x,$y + 1);

break;
case 2:
# Queen
# Downwards diagonal
for($d = 1;$x + $d < 8 &&$y + $d < 8;$d++) {
if($board[$y + $d][$x + $d] & 8) {$results[] = array($x +$d, $y +$d);

if($board[$y + $d][$x + $d] !== -1) break; } else { break; } } for($d = -1; $x +$d >= 0 && $y +$d >= 0; $d--) { if($board[$y +$d][$x +$d] & 8) {
$results[] = array($x + $d,$y + $d); if($board[$y +$d][$x +$d] !== -1) break;
} else {
break;
}
}

# Upwards diagonal
for($d = 1;$x + $d < 8 &&$y - $d >= 0;$d++) {
if($board[$y - $d][$x + $d] & 8) {$results[] = array($x +$d, $y -$d);

if($board[$y - $d][$x + $d] !== -1) break; } else { break; } } for($d = -1; $x +$d >= 0 && $y -$d < 8; $d--) { if($board[$y -$d][$x +$d] & 8) {
$results[] = array($x + $d,$y - $d); if($board[$y -$d][$x +$d] !== -1) break;
} else {
break;
}
}

# Horizontal
for($d = 1;$x + $d < 8;$d++) {
if($board[$y][$x +$d] & 8) {
$results[] = array($x + $d,$y);

if($board[$y][$x +$d] !== -1) break;
} else {
break;
}
}

for($d = -1;$x + $d >= 0;$d--) {
if($board[$y][$x +$d] & 8) {
$results[] = array($x + $d,$y);

if($board[$y][$x +$d] !== -1) break;
} else {
break;
}
}

# Vertical
for($d = 1;$y + $d < 8;$d++) {
if($board[$y + $d][$x] & 8) {
$results[] = array($x, $y +$d);

if($board[$y + $d][$x] !== -1) break;
} else {
break;
}
}

for($d = -1;$y + $d >= 0;$d--) {
if($board[$y + $d][$x] & 8) {
$results[] = array($x, $y +$d);

if($board[$y + $d][$x] !== -1) break;
} else {
break;
}
}

break;
case 3:
# Bishop
# Downwards diagonal
for($d = 1;$x + $d < 8 &&$y + $d < 8;$d++) {
if($board[$y + $d][$x + $d] & 8) {$results[] = array($x +$d, $y +$d);

if($board[$y + $d][$x + $d] !== -1) break; } else { break; } } for($d = -1; $x +$d >= 0 && $y +$d >= 0; $d--) { if($board[$y +$d][$x +$d] & 8) {
$results[] = array($x + $d,$y + $d); if($board[$y +$d][$x +$d] !== -1) break;
} else {
break;
}
}

# Upwards diagonal
for($d = 1;$x + $d < 8 &&$y - $d >= 0;$d++) {
if($board[$y - $d][$x + $d] & 8) {$results[] = array($x +$d, $y -$d);

if($board[$y - $d][$x + $d] !== -1) break; } else { break; } } for($d = -1; $x +$d >= 0 && $y -$d < 8; $d--) { if($board[$y -$d][$x +$d] & 8) {
$results[] = array($x + $d,$y - $d); if($board[$y -$d][$x +$d] !== -1) break;
} else {
break;
}
}

break;
case 4:
# Knight
if($x > 1 &&$y > 0 && $board[$y - 1][$x - 2] & 8)$results[] = array($x - 2,$y - 1);
if($x > 0 &&$y > 1 && $board[$y - 2][$x - 1] & 8)$results[] = array($x - 1,$y - 2);
if($x < 7 &&$y > 1 && $board[$y - 2][$x + 1] & 8)$results[] = array($x + 1,$y - 2);
if($x < 6 &&$y > 0 && $board[$y - 1][$x + 2] & 8)$results[] = array($x + 2,$y - 1);
if($x < 6 &&$y < 7 && $board[$y + 1][$x + 2] & 8)$results[] = array($x + 2,$y + 1);
if($x < 7 &&$y < 6 && $board[$y + 2][$x + 1] & 8)$results[] = array($x + 1,$y + 2);
if($x > 0 &&$y < 6 && $board[$y + 2][$x - 1] & 8)$results[] = array($x - 1,$y + 2);
if($x > 1 &&$y < 7 && $board[$y + 1][$x - 2] & 8)$results[] = array($x - 2,$y + 1);

break;
case 5:
# Rook
# Horizontal
for($d = 1;$x + $d < 8;$d++) {
if($board[$y][$x +$d] & 8) {
$results[] = array($x + $d,$y);

if($board[$y][$x +$d] !== -1) break;
} else {
break;
}
}

for($d = -1;$x + $d >= 0;$d--) {
if($board[$y][$x +$d] & 8) {
$results[] = array($x + $d,$y);

if($board[$y][$x +$d] !== -1) break;
} else {
break;
}
}

# Vertical
for($d = 1;$y + $d < 8;$d++) {
if($board[$y + $d][$x] & 8) {
$results[] = array($x, $y +$d);

if($board[$y + $d][$x] !== -1) break;
} else {
break;
}
}

for($d = -1;$y + $d >= 0;$d--) {
if($board[$y + $d][$x] & 8) {
$results[] = array($x, $y +$d);

if($board[$y + $d][$x] !== -1) break;
} else {
break;
}
}

break;
}

return $results; } function invertRelationship($piece) {
return $piece === -1 ? -1 :$piece ^ 8;
}

function invertPosition($position) { return array($position[0], 7 - $position[1]); } function invertBoard($board) {
$invertedBoard = array(); for($i = 7; $i > -1;$i--) {
$invertedBoard[] = array_map('invertRelationship',$board[$i]); } return$invertedBoard;
}

function validMoves($x,$y) {
global $board;$type = $board[$y][$x]; if($type & 8) {
return array_map('invertPosition', iValidMoves(invertBoard($board),$type & ~8, $x, 7 -$y));
} else {
return iValidMoves($board,$type, $x,$y);
}
}

function shouldHighlight($x,$y) {
global $highlight; foreach($highlight as $position) { if($position[0] == $x &&$position[1] == $y) { return true; } } return false; } if(isset($_SESSION['board'])) {
$board =$_SESSION['board'];
} else {
$board = array( array(5 | 8, 4 | 8, 3 | 8, 1 | 8, 2 | 8, 3 | 8, 4 | 8, 5 | 8), array(0 | 8, 0 | 8, 0 | 8, 0 | 8, 0 | 8, 0 | 8, 0 | 8, 0 | 8), array(-1, -1, -1, -1, -1, -1, -1, -1), array(-1, -1, -1, -1, -1, -1, -1, -1), array(-1, -1, -1, -1, -1, -1, -1, -1), array(-1, -1, -1, -1, -1, -1, -1, -1), array(0, 0, 0, 0, 0, 0, 0, 0), array(5, 4, 3, 1, 2, 3, 4, 5) ); }$back = array(
imagecreatefrompng('back/16.png'),  # pawn
imagecreatefrompng('back/6.png'),   # king
imagecreatefrompng('back/149.png'), # queen
imagecreatefrompng('back/37.png'),  # bishop
imagecreatefrompng('back/25.png'),  # knight
imagecreatefrompng('back/75.png')   # rook
);

$front = array( imagecreatefrompng('front/16.png'), # pawn imagecreatefrompng('front/6.png'), # king imagecreatefrompng('front/149.png'), # queen imagecreatefrompng('front/37.png'), # bishop imagecreatefrompng('front/25.png'), # knight imagecreatefrompng('front/75.png') # rook );$image = $_GET['i']; if(ctype_digit($image)) {
$image = (int)$image;
} else {
kick();
}

if($image < 1 ||$image > 64) {
kick();
}

$highlight = array();$referrer = $_SERVER['HTTP_REFERER'];$action = null;

if(strpos($referrer, '?a=') > -1) {$action = substr($referrer, strpos($referrer, '?a=') + 3);
}

if($action !== null &&$image === 1) { # Only do this once!
if(!ctype_digit($action)) kick();$action = (int)$action; if($action < 1 || $action > 64) kick();$aX = ($action - 1) % 8;$aY = floor(($action - 1) / 8); if(isset($_SESSION['selected'])) {
if($_SESSION['selected'] !==$action) {
# Make sure the piece can actually move there.
# If it can, move.
# "Highlight" the places that the piece can move:
$highlight = validMoves(($_SESSION['selected'] - 1) % 8, floor(($_SESSION['selected'] - 1) / 8)); if(shouldHighlight($aX, $aY)) { # The move is good!$sX = ($_SESSION['selected'] - 1) % 8;$sY = floor(($_SESSION['selected'] - 1) / 8);$board[$aY][$aX] = $board[$sY][$sX];$board[$sY][$sX] = -1;

# Now, rotate the board for the next person to play:
$invertedBoard = invertBoard($board);
$rotatedBoard = array(); foreach($invertedBoard as $row) { for($i = 0; $i < 4;$i++) {
$row[$i] ^= $row[7 -$i];
$row[7 -$i] ^= $row[$i];
$row[$i] ^= $row[7 -$i];
}

$rotatedBoard[] =$row;
}

$board =$rotatedBoard;
}
}

unset($_SESSION['selected']); } elseif(($board[$aY][$aX] & 8) === 0) {
# Select a piece:
$_SESSION['selected'] =$action;
}
}

if(isset($_SESSION['selected'])) { # Highlight the places that the piece can move:$highlight = validMoves(($_SESSION['selected'] - 1) % 8, floor(($_SESSION['selected'] - 1) / 8));
}

# Draw the background:
$background = imagecreatetruecolor(96, 96);$black = imagecolorallocate($background, 0, 0, 0);$white = imagecolorallocate($background, 255, 255, 255);$red = imagecolorallocatealpha($background, 255, 0, 0, 100); if(($image + floor(($image - 1) / 8)) % 2) { imagefilledrectangle($background, 0, 0, 96, 96, $black); } else { imagefilledrectangle($background, 0, 0, 96, 96, $white); } # Draw a piece, if there is one:$piece = $board[floor(($image - 1) / 8)][($image - 1) % 8]; if($piece > -1) {
if($piece & 8) {$piece &= ~8;
$draw =$front[$piece]; } else {$draw = $back[$piece];
}

imagecopy($background,$draw, 0, 0, 0, 0, 96, 96);
}

# Should we highlight this place?
if(shouldHighlight(($image - 1) % 8, floor(($image - 1) / 8))) {
imagefilledrectangle($background, 0, 0, 96, 96,$red);
}

imagepng($background);$_SESSION['board'] = \$board;
?>

• I love this, but the two sides should be different Pokemon! – MrZander Jun 10 '12 at 19:29
• Very nice. I like it that the table turns whenever the turn changes. – Cristian Lupascu Jun 11 '12 at 20:01
• And in PHP, +1 for PHP games :p – Event_Horizon Jun 21 '12 at 18:29
• @hhh: No, you add parameters to the same page and generate the image(s) on the server by checking the Referer header. – Ry- Aug 26 '12 at 19:20
• :-(. Your sprites have died. – Justin Apr 24 '14 at 18:34

## "Simon says" game

Unfortunately, I could not get this submission in on time by the (somewhat arbitrary) deadline, but I really wanted to demonstrate animation in such a user profile game, and none of the previous submissions are animated. This game is a clone of the classic Milton Bradley game Simon, in which the player tries to repeat an increasingly long sequence of signals.

Information about this game, including its source code, is available at its GitHub page. There may be occasional graphical glitches (especially on Windows computers) arising from the hackish "palette animation" that avoids the need for a graphics drawing library. The existence of these glitches can serve as a useful excuse for quickly losing this game because of terrible memory.

Additionally, the effects of high latency and limited bandwidth can make this game much more challenging than the original as well. I think that in order to get much more than five points (when the game first speeds up), you will need to determine which light flashes one more time than in the previous round rather than depending on the correct sequence (which is very hard to do).

If this game fails to work for you (it restarts every time you click a button), your browser might be blocking its cookie. I have not yet added a workaround, so for the time being, please either use Chrome, Opera, or Firefox or temporarily change your Internet Explorer or Safari cookie settings.

Edit 2018-05-24: At this time, I have deleted the publicly accessible Heroku instance of this app. I may or may not put the app back online at a later date. The app's code is still available on GitHub, so you can either run it locally or create your own Heroku app instance if you wish to play the game.

• +1 That is absolutely brilliant! Never thought of doing dynamically generated animate gifs :P – mellamokb Jul 2 '12 at 15:43