Create a User-Profile Mini-Game
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 implementedHangmanImageRenderer
- the class that encapsulates all the GDI uglinessImageResult
- a customActionResult
that is used to return a dynamically generated image
The entire code listing is available at http://pastebin.com/ccwZLknX
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.
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!
Restart levelNew game
. . ↑
← → - No-op*
. . ↓
*(click here if game becomes unresponsive)
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!