127 lines
3.5 KiB
Text
127 lines
3.5 KiB
Text
include "MRG32k3a" {search: "."}; # see comment above
|
|
|
|
### Generic functions
|
|
|
|
def lpad($len): tostring | ($len - length) as $l | (" " * $l) + .;
|
|
|
|
# Create an m x n matrix with initial values specified by .
|
|
def matrix($m; $n):
|
|
if $m == 0 then []
|
|
else . as $init
|
|
| if $m == 1 then [range(0;$n) | $init]
|
|
elif $m > 0 then
|
|
matrix(1; $n) as $row
|
|
| [range(0; $m) | $row ]
|
|
else error("matrix\($m);\($n)) invalid")
|
|
end
|
|
end;
|
|
|
|
|
|
### The 2048 Game
|
|
# Left-squish the input array in accordance with the game requirements for a single row
|
|
def squish:
|
|
def squish($n):
|
|
def s:
|
|
if length == 0 then [range(0;$n)|null]
|
|
elif .[0] == null then .[1:] | s
|
|
elif length == 1 then . + [range(0; $n - 1)|null]
|
|
elif .[0] == .[1] then [.[0] + .[1]] + (.[2:]|squish($n-1))
|
|
else .[0:1] + (.[1:]|squish($n-1))
|
|
end;
|
|
s;
|
|
squish(length);
|
|
|
|
# Input: a matrix of rows
|
|
def squish_left:
|
|
[.[] | squish];
|
|
|
|
def squish_right:
|
|
[.[] | reverse | squish | reverse];
|
|
|
|
def squish_up:
|
|
transpose
|
|
| [.[] | squish]
|
|
| transpose;
|
|
|
|
def squish_down:
|
|
transpose
|
|
| [.[] | reverse | squish | reverse]
|
|
| transpose;
|
|
|
|
# Gather the [i,j] co-ordinates of all the null values in the input matrix
|
|
def gather:
|
|
if length == 0 then error end
|
|
| (.[0] | length) as $cols
|
|
| [range(0;length) as $i
|
|
| range(0; $cols) as $j
|
|
| select(.[$i][$j] == null) | [$i,$j]]
|
|
| if length == 0 then error end ;
|
|
|
|
# Input: {matrix}
|
|
# Add a random 2 or 4 if possible, else error
|
|
# Output: includes .prng for the PRN generator
|
|
def add_random:
|
|
(.matrix | gather) as $gather
|
|
| ($gather | length) as $n
|
|
| if $n == 0 then error end
|
|
| if .prng == null then .prng = seed(now | tostring | sub("^.*[.]";"") | tonumber) end
|
|
| .prng |= nextFloat
|
|
| $gather[.prng.nextFloat * $n | trunc] as [$i,$j]
|
|
| .prng |= nextFloat
|
|
| (if (.prng.nextFloat) < 0.1 then 4 else 2 end) as $exmachina
|
|
| .matrix[$i][$j] = $exmachina ;
|
|
|
|
def prompt: "[ijkl] or [wasd] or q to quit or n to restart:";
|
|
|
|
def direction:
|
|
{"i": "up", "j": "left", "k": "down", "l": "right",
|
|
"w": "up", "a": "left", "s": "down", "d": "right",
|
|
"q": "quit",
|
|
"n": "restart"
|
|
};
|
|
|
|
# Recognize $goal as the goal
|
|
def play($goal):
|
|
|
|
# Pretty print
|
|
def pp:
|
|
.matrix[] | map(. // "." | lpad(4)) | join(" ");
|
|
|
|
def won:
|
|
any(.matrix[][]; . == $goal);
|
|
|
|
def lost:
|
|
.matrix
|
|
| (squish_left == .) and (squish_right == .) and
|
|
(squish_up == .) and (squish_down == .);
|
|
|
|
def round:
|
|
pp,
|
|
if lost then "Sorry! Better luck next time.", play($goal)
|
|
else prompt,
|
|
( (try input catch halt) as $in
|
|
| .matrix as $matrix # for checking if a move is legal
|
|
| .emit = null
|
|
| direction[$in | ascii_downcase] as $direction
|
|
| if $direction == "up" then .matrix |= squish_up
|
|
elif $direction == "down" then .matrix |= squish_down
|
|
elif $direction == "left" then .matrix |= squish_left
|
|
elif $direction == "right" then .matrix |= squish_right
|
|
elif $direction == "quit" then .emit = "bye"
|
|
elif $direction == "restart" then .emit = "restart"
|
|
else .emit = "unknown direction \($direction) ... please try again."
|
|
end
|
|
| if .emit == "bye" then .emit, halt
|
|
elif .emit == "restart" then "Restarting...", play($goal)
|
|
elif .emit != null then .emit, round
|
|
elif .matrix == $matrix then "Disallowed direction! Please try again.", round
|
|
elif won then pp, "Congratulations!", play($goal)
|
|
else add_random | round
|
|
end)
|
|
end;
|
|
|
|
{ matrix: (null | matrix(4;4))}
|
|
| .matrix[2] = [null,2,2,2]
|
|
| round;
|
|
|
|
play(2048)
|