267 lines
6.7 KiB
Pony
267 lines
6.7 KiB
Pony
use "term"
|
|
use "random"
|
|
use "time"
|
|
|
|
interface EdgeRow
|
|
fun val row() : Iterator[U32] ref
|
|
fun val inc() : I32
|
|
|
|
primitive TopRow is EdgeRow
|
|
fun row() : Iterator[U32] ref => let r : Array[U32] box = [0,1,2,3]
|
|
r.values()
|
|
fun inc() : I32 => 4
|
|
|
|
primitive LeftRow is EdgeRow
|
|
fun row() : Iterator[U32] ref => let r : Array[U32] box = [0,4,8,12]
|
|
r.values()
|
|
fun inc() : I32 => 1
|
|
|
|
primitive RightRow is EdgeRow
|
|
fun row() : Iterator[U32] ref => let r : Array[U32] box = [3,7,11,15]
|
|
r.values()
|
|
fun inc() : I32 => -1
|
|
|
|
primitive BottomRow is EdgeRow
|
|
fun row() : Iterator[U32] ref => let r : Array[U32] box = [12,13,14,15]
|
|
r.values()
|
|
fun inc() : I32 => -4
|
|
|
|
primitive LEFT
|
|
primitive RIGHT
|
|
primitive UP
|
|
primitive DOWN
|
|
type Move is (LEFT|RIGHT|UP|DOWN)
|
|
|
|
class KeyboardHandler is ANSINotify
|
|
let _game : Game tag
|
|
new iso create(game : Game tag) => _game = game
|
|
|
|
fun ref apply(term: ANSITerm ref, input: U8 val) =>
|
|
if input == 113 then
|
|
_game.quit()
|
|
term.dispose()
|
|
end
|
|
fun ref left(ctrl: Bool, alt: Bool, shift: Bool) => _game.move(LEFT)
|
|
fun ref down(ctrl: Bool, alt: Bool, shift: Bool) => _game.move(DOWN)
|
|
fun ref up(ctrl: Bool, alt: Bool, shift: Bool) => _game.move(UP)
|
|
fun ref right(ctrl: Bool, alt: Bool, shift: Bool) => _game.move(RIGHT)
|
|
|
|
type ROW is (U32,U32,U32,U32)
|
|
|
|
primitive Merger
|
|
fun tag apply(r : ROW) : ROW =>
|
|
match r
|
|
| (0,0,0,_) => (r._4,0,0,0)
|
|
| (0,0,_,r._3) => (r._3<<1,0,0,0)
|
|
| (0,0,_,_) => (r._3,r._4,0,0)
|
|
| (0,_,r._2,_) => (r._2<<1,r._4,0,0)
|
|
| (0,_,0,r._2) => (r._2<<1,0,0,0)
|
|
| (0,_,0,_) => (r._2,r._4,0,0)
|
|
| (0,_,_,r._3) => (r._2,r._3<<1,0,0)
|
|
| (0,_,_,_) => (r._2,r._3,r._4,0)
|
|
| (_, r._1, _, r._3) => (r._1<<1, r._3<<1, 0, 0)
|
|
| (_, r._1, 0, _) => (r._1<<1, r._4, 0, 0)
|
|
| (_, r._1, _, _) => (r._1<<1, r._3, r._4, 0)
|
|
| (_, 0,r._1, _) => (r._1<<1,r._4,0,0)
|
|
| (_, 0,0, r._1) => (r._1<<1,0,0,0)
|
|
| (_, 0,0, _) => (r._1,r._4,0,0)
|
|
| (_, 0,_, r._3) => (r._1, r._3<<1,0,0)
|
|
| (_, 0,_, _) => (r._1, r._3,r._4,0)
|
|
| (_,_,r._2,_) => (r._1, r._2<<1,r._4,0)
|
|
| (_,_,0,r._2) => (r._1, r._2<<1,0,0)
|
|
| (_,_,0,_) => (r._1, r._2,r._4,0)
|
|
| (_,_,_,r._3) => (r._1, r._2,r._3<<1,0)
|
|
else
|
|
r
|
|
end
|
|
/**
|
|
* Game actor
|
|
*/
|
|
actor Game
|
|
embed _grid : Array[U32] = Array[U32].init(0, 16)
|
|
let _rand : Random = MT(Time.millis())
|
|
let _env : Env
|
|
let _board : String ref = recover String(1024) end
|
|
|
|
new create(env: Env)=>
|
|
_env = env
|
|
_add_block()
|
|
_add_block()
|
|
_draw()
|
|
|
|
fun _merge(start : U32, inc : I32) : (ROW | None) =>
|
|
var st = start.i32()
|
|
let rval : ROW = (_get(st), _get(st + inc),
|
|
_get(st + (inc * 2)), _get(st + (inc * 3)))
|
|
let rout = Merger(rval)
|
|
if rout is rval then None else rout end
|
|
|
|
fun ref _update(start : U32, inc : I32) : Bool =>
|
|
match _merge(start, inc)
|
|
| let rout : ROW =>
|
|
var st = start.i32()
|
|
_set(st, rout._1)
|
|
_set(st + inc, rout._2)
|
|
_set(st + (inc * 2), rout._3)
|
|
_set(st + (inc * 3), rout._4)
|
|
true
|
|
else
|
|
false
|
|
end
|
|
|
|
fun ref _shift_to(edge : EdgeRow val) : Bool =>
|
|
var updated = false
|
|
for r in edge.row() do
|
|
if _update(r, edge.inc()) then
|
|
updated = true
|
|
end
|
|
end
|
|
updated
|
|
|
|
fun _fmt(i : U32) : String =>
|
|
match i
|
|
| 0 => " __ "
|
|
| 2 => "\x1B[31m 2 \x1B[0m"
|
|
| 4 => "\x1B[32m 4 \x1B[0m"
|
|
| 8 => "\x1B[33m 8 \x1B[0m"
|
|
| 16 => "\x1B[34m 16 \x1B[0m"
|
|
| 32 => "\x1B[35m 32 \x1B[0m"
|
|
| 64 => "\x1B[36m 64 \x1B[0m"
|
|
| 128 => "\x1B[37m128 \x1B[0m"
|
|
| 256 => "\x1B[41m\x1B[37m256 \x1B[0m"
|
|
| 512 => "\x1B[42m\x1B[37m512 \x1B[0m"
|
|
| 1024 => "\x1B[43m\x1B[37m1024\x1B[0m"
|
|
| 2048 => "\x1B[47m\x1B[35m\x1B[1m\x1B[5m2048\x1B[0m"
|
|
else
|
|
i.string()
|
|
end
|
|
|
|
fun ref _draw() =>
|
|
let s : String ref = _board
|
|
s.truncate(0)
|
|
var i : U32 = 0
|
|
repeat
|
|
if (i % 4) == 0 then
|
|
s.append("---------------------\n")
|
|
end
|
|
s.append(_fmt(_get(i)))
|
|
s.append(" ")
|
|
i = i + 1
|
|
if (i % 4) == 0 then
|
|
s.append("\n")
|
|
end
|
|
until i==16 end
|
|
_env.out.print(s.string())
|
|
_env.out.print("Arrow keys to move. Press (q)uit key to quit.")
|
|
|
|
fun ref _set(i:(I32|U32), v : U32) =>
|
|
try
|
|
_grid.update(i.usize(),v)
|
|
else
|
|
_env.out.print("cant update!")
|
|
end
|
|
|
|
fun _count() : U64 =>
|
|
var c : U64 = 0
|
|
for v in _grid.values() do
|
|
c = c + if v == 0 then 0 else 1 end
|
|
end
|
|
c
|
|
|
|
fun ref _add_block() =>
|
|
let c = _count()
|
|
if c == 16 then return end
|
|
|
|
var hit = _rand.int(16 - c)
|
|
var i : U32 = 0
|
|
while i < 16 do
|
|
if (_get(i) == 0) then
|
|
if hit == 0 then
|
|
_set(i, if _rand.int(10) > 0 then 2 else 4 end)
|
|
break
|
|
end
|
|
hit = hit - 1
|
|
end
|
|
i = i + 1
|
|
end
|
|
|
|
fun _get(i : (I32|U32)) : U32 => try _grid(i.usize()) else 0 end
|
|
|
|
fun _win() : Bool =>
|
|
for v in _grid.values() do
|
|
if v == 2048 then return true end
|
|
end
|
|
false
|
|
|
|
fun _no_moves(edge : EdgeRow val) : Bool =>
|
|
for r in edge.row() do
|
|
match _merge(r, edge.inc())
|
|
| let rout : ROW =>
|
|
if (rout._1 == 0) or (rout._2 == 0) or
|
|
(rout._3 == 0) or (rout._4 == 0) then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
true
|
|
|
|
fun _lose() : Bool =>
|
|
(_grid.size() >= 16) and
|
|
_no_moves(LeftRow) and
|
|
_no_moves(RightRow) and
|
|
_no_moves(TopRow) and
|
|
_no_moves(BottomRow)
|
|
|
|
be quit()=>
|
|
_env.out.print("Exiting.. some terminals may require <ctrl-c>")
|
|
_env.exitcode(0)
|
|
_env.input.dispose()
|
|
|
|
be move(m: Move) =>
|
|
let updated =
|
|
match m
|
|
| LEFT => _shift_to(LeftRow)
|
|
| RIGHT => _shift_to(RightRow)
|
|
| UP => _shift_to(TopRow)
|
|
| DOWN => _shift_to(BottomRow)
|
|
else
|
|
false
|
|
end
|
|
|
|
if _win() then
|
|
_draw()
|
|
_env.out.print("You win :)")
|
|
quit()
|
|
else
|
|
if updated then
|
|
_add_block()
|
|
_draw()
|
|
end
|
|
if _lose() then
|
|
_env.out.print("You lose :(")
|
|
quit()
|
|
end
|
|
end
|
|
|
|
actor Main
|
|
new create(env: Env) =>
|
|
// unit test
|
|
ifdef "test" then
|
|
TestMain(env)
|
|
return
|
|
end
|
|
// else game
|
|
let input : Stdin tag = env.input
|
|
env.out.print("Welcome to ponylang-2048...")
|
|
let game = Game(env)
|
|
let term = ANSITerm(KeyboardHandler(game), input)
|
|
|
|
let notify : StdinNotify iso = object iso
|
|
let term: ANSITerm = term
|
|
let _in: Stdin tag = input
|
|
fun ref apply(data: Array[U8] iso) => term(consume data)
|
|
fun ref dispose() => _in.dispose()
|
|
end
|
|
|
|
input(consume notify)
|