Create a User-Profile Mini-Game

49

20

Yesterday, I stumbled on a very clever thing.

minitech's tic-tac-toe profile game

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

mellamokb's towers of hanoi profile game

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:

Towers of Hanoi

http://hanoi.kurtbachtold.com/hanoi.php/text

http://hanoi.kurtbachtold.com/hanoi.php/1 http://hanoi.kurtbachtold.com/hanoi.php/2 http://hanoi.kurtbachtold.com/hanoi.php/3

Reset

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.

mellamokb

Posted 2012-05-23T14:11:59.657

Reputation: 5 544

1+1 Simple, but brilliant idea! BTW - for the deadline I think you mean June 2, 2012. – Cristian Lupascu – 2012-05-23T15:22:09.163

Derp, yes I did, thanks :) – mellamokb – 2012-05-23T15:23:09.710

Short deadline + already answered your own question = downvote. – boothby – 2012-05-23T18:50:07.717

1@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 – 2012-05-23T19:02:46.480

4I 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 – 2012-05-23T23:04:25.810

@mellamokb, the later you make the deadline (within reason), the more answers and more variety you'll get. I'd say set it for Canada Day. – boothby – 2012-05-24T01:35:08.837

@boothby, There was just a recent SE blogpost reminding people that they are encouraged to answer their own questions. – breadbox – 2012-05-24T06:57:56.880

@breadbox: That was probably more relevant for SO than here. I agree with boothby that my posting an answer could be seen as an unfair way to win/get votes, so I removed it and placed my example inside the question instead. – mellamokb – 2012-05-24T13:23:50.507

@boothby: No I agree, I'll extend it out some more. I didn't want to make the contest too long, but I think end of June is fine. I'll update. – mellamokb – 2012-05-24T13:25:35.743

Maybe I'm being stupid, but none of these work for me (Chrome 19.0.1084.46 beta-m) – Griffin – 2012-06-02T22:09:14.813

I'm on Chrome 19.0.1084.52 and have no issue. What are you seeing? – mellamokb – 2012-06-03T01:47:39.327

Neither example works in Firefox 12 on Windows 7. What are the browser requirements? – Mr.Wizard – 2012-06-04T08:54:54.413

1@Mr.Wizard: working fine on FF 12.0 & Windows 7 here, could you post more details about what's not working? – ChristopheD – 2012-06-04T13:36:03.303

@Mr.Wizard: If you look at some of the examples below, they should all work because they have a single image. As I mention in my post, since I have 4 images, there are flukey things that happen, where it doesn't appear to work but it actually has. My updates are applied specifically when the image with text (first image) is loaded. If that image is loaded last due to how the browser loads the images, it will appear that nothing has happened, but the state of the game actually has changed if you reload the page (i.e., click the question title to reload) or click another peg. – mellamokb – 2012-06-04T13:40:07.983

You might have to allow 3rd party cookies to get Hangman and Maze working (my game should work fine without it; I recommend blocking them again afterwards for privacy reasons). ChristopheD's and my game also need a browser that allows svg in img tags

– copy – 2012-06-04T16:16:07.897

@copy: I switched to PNG output for compatibility purposes, and the 3rd party cookie (which was really only a problem with IE with most browser's default settings) should be solved now as well afaik. – ChristopheD – 2012-06-04T17:34:13.097

@ChristopheD I think P3P policies only work for IE and Safari. I can't play Maze or Hangman without changing my settings at least (Chromium 17) – copy – 2012-06-04T19:42:45.153

Yeah, this was randomly thrown together. Chess coming up next, guys! Also, nice job on the towers of Hanoi. It looks better. – Ry- – 2012-06-10T14:15:22.457

@minitech: I especially liked your secret message in TTT if you try to hack :P – mellamokb – 2012-06-11T13:57:05.863

Answers

27

Conway's Game of Life

+1 generation - +5 generations - zoom in - zoom out

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)

def read_pattern(filename, offset_x, offset_y):

    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):


    if 'REMOTE_ADDR' in env:
        client_ip = env['REMOTE_ADDR']
    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():
                session[0:2] = read_pattern(filename, 10, 10)
        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 '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" width="400px" height="200px">'
    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.

copy

Posted 2012-05-23T14:11:59.657

Reputation: 6 466

+1 Awesome! One suggestion: append #5946 to your links, and it will jump back to your post after every update. – mellamokb – 2012-05-23T20:11:12.623

hmm.. at least it worked when i tried it.. ah. because in Towers of Hanoi you're always clicking on different pegs. hmm – mellamokb – 2012-05-23T20:12:27.393

@mellamokb it works, but you can't click the same link twice now – copy – 2012-05-23T20:13:30.970

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 – 2012-05-23T20:13:51.963

Regarding browser does not load images in order, that's the implementation detail that is troubling me. I'm thinking there needs to be a way to have the first image requested (which ever one it is) do all the session updates, and the rest of the images just load. But it's difficult to track this on the server side, especially if the requests look identical. – mellamokb – 2012-05-24T13:46:00.640

It would be really awesome if someone could make this a dynamically-rendered animated GIF. – Ry- – 2012-06-27T16:39:00.790

Accepted your answer as it has the most votes. Although to be fair, that's probably partly because yours was the oldest answer :) – mellamokb – 2012-07-02T15:45:44.107

1@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 – 2012-07-02T19:09:19.747

35

C# - Stack Exchange Hangman

Guess names of Stack Exchange websites in this Hangman game:



A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
New 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

Cristian Lupascu

Posted 2012-05-23T14:11:59.657

Reputation: 8 369

+1 Wow, you guys are awesome :). Like the ideas so far! – mellamokb – 2012-05-24T20:24:09.797

Cool, never heard of appharbor.com. Are you really paying to host your answer? – mellamokb – 2012-05-24T20:25:25.397

@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 – 2012-05-24T20:29:18.137

2I was going to mention if necessary, I can provide custom subdomain hosting and FTP access to my hosting site. – mellamokb – 2012-05-24T20:32:15.430

@mellamokb thanks, but I think this hosting will do. I was just joking about many clicks. :) – Cristian Lupascu – 2012-05-25T06:34:59.327

You could add the following header to make your solution work in IE (with default Medium Privacy settings, won't accept cookies from your image otherwise): P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR" – ChristopheD – 2012-06-04T10:43:24.580

@ChristopheD Wow, I wasn't even aware that this does not work on IE. Thanks a lot, I made the modification you suggested and it works fine now (at least in IE9) – Cristian Lupascu – 2012-06-04T12:22:38.387

19

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!

Clojoban! Restart levelNew game

. .

- No-op*

. .

**(click here if game becomes unresponsive)*

Instructions

You are Robby Robby, a hard-working robot. You work at a FlipCo Industries as a heavy load carrier. Your job is to move each box A box to a goal 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!

Álvaro Cuesta

Posted 2012-05-23T14:11:59.657

Reputation: 311

Now your reputation is 11 :) – mellamokb – 2012-06-16T14:04:22.683

@mellamokb thanks! Game is embedded now :) – Álvaro Cuesta – 2012-06-16T16:23:48.563

Apparently this didn't get much attention. Any tips for improving? – Álvaro Cuesta – 2012-06-21T15:41:14.573

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 – 2012-06-21T17:27:55.943

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 – 2012-06-23T10:48:12.860

@w0lf there's a standalone version at Clojoban's front page. You can even use your arrow keys to move there, which makes the game far more playable :)

– Álvaro Cuesta – 2012-06-23T16:09:18.470

@ÁlvaroCuesta Just finished it. Excellent! The ice is a great twist to the classic Sokoban. This would also be great for a mobile phone game :) – Cristian Lupascu – 2012-06-23T16:23:03.167

How did you get the game working in your SO -profile? The game is in image -element and then with POST -request played? I tried already the labyrithm but did not get it working in the profile :( – hhh – 2012-08-26T19:15:01.463

It appears this game has stopped working :( – mellamokb – 2012-11-20T22:32:50.300

@hhh look at the address bar! The SO-side querystring is read in the referer header. Persistence is handled game-side with sessions. – kaoD – 2013-03-08T02:19:09.777

@mellamokbtheWise apparently it fixed itself. – kaoD – 2013-03-08T02:26:22.620

You want a good level? Take a look at these: http://nethackwiki.com/wiki/Sokoban they do have too many boxes though.

– Justin – 2014-04-24T18:39:51.523

17

Maze

http://phpshizzle.t15.org/sogolf_maze/maze.php
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);
header("Content-Type: image/png");
imagepng($img);
imagedestroy($img);
?>

ChristopheD

Posted 2012-05-23T14:11:59.657

Reputation: 1 599

1+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 – 2012-06-02T20:56:11.043

@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: http://stackoverflow.com/users/81179/christophed)

– ChristopheD – 2012-06-02T21:16:34.370

I would fancy a simple no-operation link (refresh?) to make the update easier when trying to move twice in the same direction :) – kaoD – 2012-06-04T17:04:13.870

@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 – 2012-06-04T17:32:00.520

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 – 2012-06-04T17:50:29.337

@kaoD: the URL's are part of the answer here, I can't easily change them (each iteration). Or do I misunderstand? AFAIK the other solutions suffer the same shortcomings (when using anchor links to point to the right answer). – ChristopheD – 2012-06-04T18:44:05.187

@ChristopheD I mean you just need a link to http://codegolf.stackexchange.com/questions/5933/create-a-user-profile-mini-game/6171?noop#answer-6171 and clicking it will refresh the page, keep the anchor and will let you click the same option again.

– kaoD – 2012-06-04T23:37:40.637

@kaoD: I've included a noop part as you suggested, but I still don't see how that would help (the second time) since I am obliged to use fixed links in my answer... – ChristopheD – 2012-06-05T06:53:52.253

@ChristopheD wow, I'm having a hard time making it clear :P If you put that link apart (not for each of your links) the user can click it to avoid pressing F5 (which doesn't always work for me, btw.) Since the URL changes (and your game does nothing, since it's a non-operative link) you can click again the link that didn't work before! – kaoD – 2012-06-05T16:30:28.573

@kaoD: sorry for begin so slow in understanding what you meant, I've added a <kbd>noop</kbd> button. – ChristopheD – 2012-06-06T14:18:48.220

Sorry I am total noob but I tried to get a next move like this here, why does it not work? I am apparently misunderstanding the POST -request? I copy-pasted this code to my server with PHP support, maze here works, but the POST -controls not working with your example, why?

– hhh – 2012-08-20T21:30:29.403

14

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() {
    header("Status: Forbidden\r\n", true, 403);
    header("Content-Type: text/plain\r\n");
    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);
}

header("Content-Type: image/png\r\n");

imagepng($background);

$_SESSION['board'] = $board;
?>

Ry-

Posted 2012-05-23T14:11:59.657

Reputation: 5 283

I love this, but the two sides should be different Pokemon! – MrZander – 2012-06-10T19:29:59.050

Very nice. I like it that the table turns whenever the turn changes. – Cristian Lupascu – 2012-06-11T20:01:01.917

1And in PHP, +1 for PHP games :p – Event_Horizon – 2012-06-21T18:29:45.380

What is the basic idea of deploying the game in the user -profile? Do you generate an image of the game to display it in the user -profile and then add POST -commands to change the picture? – hhh – 2012-08-26T19:19:05.187

1@hhh: No, you add parameters to the same page and generate the image(s) on the server by checking the Referer header. – Ry- – 2012-08-26T19:20:41.143

Is the idea the same as with the Maze? I have got it more working than this (Go away -text because not downloaded the sprites) but still at the starting point: here and here. When I try to move things there with POST command such as "layerdown", it is doing nothing -- and image not displayed in profile, cannot understand why.

– hhh – 2012-08-26T19:31:59.883

@hhh: There is no POST involved. The information comes from the Referer header. – Ry- – 2012-08-26T20:58:13.257

I cannot understand the difference, both transferred in the URL visible to the user? This wiki here about the Referer, thank you, have to study this a bit further. Not sure which one is better here for a profile -game. I wish I could get some small example working though without too much hxcking :)

– hhh – 2012-08-26T22:02:58.530

5:-(. Your sprites have died. – Justin – 2014-04-24T18:34:58.600

10

"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.

PleaseStand

Posted 2012-05-23T14:11:59.657

Reputation: 5 369

+1 That is absolutely brilliant! Never thought of doing dynamically generated animate gifs :P – mellamokb – 2012-07-02T15:43:07.340

2

Rock, Paper, Scissors

All links go to my profile page for speed.

The Game

timmyRS

Posted 2012-05-23T14:11:59.657

Reputation: 329