
Dung-beetle style development - The feeling you get working on a code base where each change feels like pushing an ever bigger ball of crap.
A phrase I remembered from working with Roly that made me laugh!
data Cell = Off
| On
| Dying
deriving (Eq,Show)
type GameGrid = Array (Int,Int) Cell
type Neighbours = [Cell]
data Game = Game GameGrid Int deriving Show
type
is just used to give friendly names to types so I don't forget what they are!cellToChar
I'm sure there must be a way of encoding this information so that I don't have to explictly write out each case? I realise I could define Cell as type data Cell = State Char
, but that still doesn't feel write as it's only for three specific instances of Char
. Anyway!cellToChar :: Cell -> Char
cellToChar Off = '0'
cellToChar On = '1'
cellToChar Dying = '2'
charToCell :: Char -> Cell
charToCell '0' = Off
charToCell '1' = On
charToCell '2' = Dying
charToCell _ = error "Undefined character received"
createGame :: Int -> [Cell] -> Game
createGame x c = Game (listArray ((0,0),(x-1,x-1)) c) x
gridToString :: Game -> String
gridToString (Game g _) = map cellToChar (elems g)
processMessage :: String -> String
processMessage s = map cellToChar newGrid where
[cellSizeStr,original] = lines s
cells = map charToCell original
cellSize = read cellSizeStr :: Int
newGrid = step (createGame cellSize cells)
listenLoop :: Handle -> IO ()
listenLoop h = do
msg <- readFrame h
sendFrame h (processMessage msg)
listenLoop h
main :: IO ()
main = serverListen 9876 listenLoop
rules :: Cell -> Neighbours -> Cell
rules On _ = Dying
rules Off cells | length (filter (/= Off) cells) == 2 = On
| otherwise = Off
rules Dying _ = Off
neighbours
by assuming it wraps around. neighbours :: Game -> (Int,Int) -> Neighbours
neighbours (Game c s) (x,y) = [c ! ((x+dx) `mod` s, (y+dy) `mod` s)
| dx <- [-1,0,1], dy <- [-1,0,1], dx /= dy]
step :: Game -> [Cell]
step g@(Game c s) = [ rules (c ! p) (neighbours g p) | p <- coords] where
coords = [(x,y) | x <- [0..(s-1)], y <- [0..(s-1)]]
ffmpeg
you can create a video like this (assuming your files are named 001.jpg, 002.jpg and so on).
ffmpeg -r 3 -b 1800 -i %03d.jpg output.mp4
if ('WebSocket' in window) {
ws = new WebSocket('ws://localhost:9876/');
ws.onopen = function() {
$('#connectionStatus').text('Connection opened');
}
ws.onclose = function() {
$('#connectionStatus').text('Connection closed');
}
ws.onmessage = function(evt) {
updateGrid(evt.data);
drawGrid();
return true;
}
}
$('#step').bind('click', function() { runStep(ws); });
function runStep(ws) {
var cells = '';
var pos = 0;
for (i=0;i<cellCount;++i) {
for (j=0;j<cellCount;++j) {
cells += grid[i][j];
}
}
ws.send(cellCount + '\n' + cells);
}
acceptLoop
function accept an additional argument which handles communication back and forth.
module Web (serverListen, sendFrame, readFrame) where
acceptLoop :: Socket -> (Handle -> IO ()) -> IO a
acceptLoop socket f = forever $ do
(h,_,_) <- accept socket
hPutStr h serverHandshake
hSetBuffering h NoBuffering
forkIO (f h)
listenLoop :: Handle -> IO ()
listenLoop h = do
msg <- readFrame h
sendFrame h msg
listenLoop h
main :: IO ()
main = serverListen 9876 listenLoop
hGetContents
and just read off one message at a time. This is doomed to failure because laziness and hanging around on a network socket that's in a semi-closed state just isn't going to work.Control.Monad.when
)
listenLoop :: Handle -> IO ()
listenLoop h = do
sendFrame h "hi, remember you can stop this at anytime by pressing quit!"
msg <- readFrame h
putStrLn msg
when (msg /= "quit") (listenLoop h)
readFrame :: Handle -> IO String
readFrame h = readUntil h ""
where
readUntil h str = do
new <- hGetChar h
if (new == chr 0)
then readUntil h ""
else if new == chr 255
then return str
else readUntil h (str ++ [new])
ws
object has the appropriate scope!
<body>
<h1>I'm doing something</h1>
<div id="output">
</div>
<div id="connectionStatus">
</div>
<textarea rows="2" cols="80" id="message">
Type whatever you want here, but type quit to close the connection
</textarea>
<br />
<button id="clickMe" onClick="ws.send($('#message').val());">
Click me!
</button>
</body>
/etc/apache2/sites-available/default
and make it point to the appropriate place (or copy it and create your own configuration). Apache can be restarted with apache2ctl restart
.
import Network
import System.IO
import Control.Concurrent
import Char
serverHandshake =
"HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
\Upgrade: WebSocket\r\n\
\Connection: Upgrade\r\n\
\WebSocket-Origin: http://localhost\r\n\
\WebSocket-Location: ws://localhost:9876/\r\n\
\WebSocket-Protocol: sample\r\n\r\n"
acceptLoop socket = forever $ do
(h,_,_) <- accept socket
hPutStr h serverHandshake
hSetBuffering h NoBuffering
forkIO (listenLoop h)
where
forever a = do a; forever a
main = withSocketsDo $ do
socket <- listenOn (PortNumber 9876)
acceptLoop socket
sClose socket
return ()
listenLoop :: Handle -> IO ()
listenLoop h = do
sendFrame h "hello from haskell"
threadDelay (3 * 1000000)
sendFrame h "it works!"
return ()
sendFrame :: Handle -> String -> IO ()
sendFrame h s = do
hPutChar h (chr 0)
hPutStr h s
hPutChar h (chr 255)
forkIO
makes handling the socket on a separate thread trivial, it spawns off a new lightweight thread (lightweight in the sense of being very cheap to create) to handle the IO processing.
<html>
<head>
<title>Web Sockets</title>
<script src="http://www.google.com/jsapi"></script>
<script>
google.load('jquery','1.3.2');
</script>
<script>
$(document).ready(function() {
if ("WebSocket" in window) {
var ws = new WebSocket("ws://localhost:9876/");
ws.onopen = function() {
$('#connectionStatus').text('Connection opened');
};
ws.onmessage = function(evt) {
$('#output').append('<p>' + evt.data);
};
ws.onclose = function() {
$('#connectionStatus').text('Connection closed');
};
}
else {
$('#connectionStatus').append('<p>Your browser does not support web sockets</p>');
}
});
</script>
</head>
<body>
<h1>I'm doing something</h1>
<div id="output">
</div>
<div id="connectionStatus">
</div>
</body>
</html>