16 puzzle game
- Task
Create 16 Puzzle Game.
See details: 16 Puzzle Game
Video: 16 Puzzle Game
- Related task
AutoHotkey
With Solver, See 16_puzzle_game/autohotkey
Go
<lang go>package main
import (
"bufio" "fmt" "log" "math/rand" "os" "strings" "time"
)
const (
easy = 1 hard = 4
)
var n [16]int
func initGrid() {
for i := 0; i < 16; i++ { n[i] = i + 1 }
}
func setDiff(level int) {
moves := 3 if level == hard { moves = 12 } rc := make([]int, 0, 4) for i := 0; i < moves; i++ { rc = rc[:0] r := rand.Intn(2) s := rand.Intn(4) if r == 0 { // rotate random row for j := s * 4; j < (s+1)*4; j++ { rc = append(rc, j) } } else { // rotate random column for j := s; j < s+16; j += 4 { rc = append(rc, j) } } var rca [4]int copy(rca[:], rc) rotate(rca) if hasWon() { // do it again i = -1 } } fmt.Println("Target is", moves, "moves.")
}
func drawGrid() {
fmt.Println() fmt.Println(" D1 D2 D3 D4") fmt.Println(" ╔════╦════╦════╦════╗") fmt.Printf("R1 ║ %2d ║ %2d ║ %2d ║ %2d ║ L1\n", n[0], n[1], n[2], n[3]) fmt.Println(" ╠════╬════╬════╬════╣") fmt.Printf("R2 ║ %2d ║ %2d ║ %2d ║ %2d ║ L2\n", n[4], n[5], n[6], n[7]) fmt.Println(" ╠════╬════╬════╬════╣") fmt.Printf("R3 ║ %2d ║ %2d ║ %2d ║ %2d ║ L3\n", n[8], n[9], n[10], n[11]) fmt.Println(" ╠════╬════╬════╬════╣") fmt.Printf("R4 ║ %2d ║ %2d ║ %2d ║ %2d ║ L4\n", n[12], n[13], n[14], n[15]) fmt.Println(" ╚════╩════╩════╩════╝") fmt.Println(" U1 U2 U3 U4\n")
}
func rotate(ix [4]int) {
last := n[ix[3]] for i := 3; i >= 1; i-- { n[ix[i]] = n[ix[i-1]] } n[ix[0]] = last
}
func hasWon() bool {
for i := 0; i < 16; i++ { if n[i] != i+1 { return false } } return true
}
func check(err error) {
if err != nil { log.Fatal(err) }
}
func main() {
initGrid() rand.Seed(time.Now().UnixNano()) level := easy scanner := bufio.NewScanner(os.Stdin) for { fmt.Print("Enter difficulty level easy or hard E/H : ") scanner.Scan() diff := strings.ToUpper(strings.TrimSpace(scanner.Text())) if diff != "E" && diff != "H" { fmt.Println("Invalid response, try again.") } else { if diff == "H" { level = hard } break } } check(scanner.Err()) setDiff(level) var ix [4]int fmt.Println("When entering moves, you can also enter Q to quit or S to start again.") moves := 0
outer:
for { drawGrid() if hasWon() { fmt.Println("Congratulations, you have won the game in", moves, "moves!!") return } for { fmt.Println("Moves so far =", moves, "\n") fmt.Print("Enter move : ") scanner.Scan() move := strings.ToUpper(strings.TrimSpace(scanner.Text())) check(scanner.Err()) switch move { case "D1", "D2", "D3", "D4": c := int(move[1] - 49) ix[0] = 0 + c ix[1] = 4 + c ix[2] = 8 + c ix[3] = 12 + c rotate(ix) moves++ continue outer case "L1", "L2", "L3", "L4": c := int(move[1] - 49) ix[0] = 3 + 4*c ix[1] = 2 + 4*c ix[2] = 1 + 4*c ix[3] = 0 + 4*c rotate(ix) moves++ continue outer case "U1", "U2", "U3", "U4": c := int(move[1] - 49) ix[0] = 12 + c ix[1] = 8 + c ix[2] = 4 + c ix[3] = 0 + c rotate(ix) moves++ continue outer case "R1", "R2", "R3", "R4": c := int(move[1] - 49) ix[0] = 0 + 4*c ix[1] = 1 + 4*c ix[2] = 2 + 4*c ix[3] = 3 + 4*c rotate(ix) moves++ continue outer case "Q": return case "S": initGrid() setDiff(level) moves = 0 continue outer default: fmt.Println("Invalid move, try again.") } } }
}</lang>
- Output:
Sample game:
Enter difficulty level easy or hard E/H : e Target is 3 moves. When entering moves, you can also enter Q to quit or S to start again. D1 D2 D3 D4 ╔════╦════╦════╦════╗ R1 ║ 1 ║ 2 ║ 3 ║ 4 ║ L1 ╠════╬════╬════╬════╣ R2 ║ 7 ║ 8 ║ 5 ║ 6 ║ L2 ╠════╬════╬════╬════╣ R3 ║ 9 ║ 10 ║ 11 ║ 12 ║ L3 ╠════╬════╬════╬════╣ R4 ║ 16 ║ 13 ║ 14 ║ 15 ║ L4 ╚════╩════╩════╩════╝ U1 U2 U3 U4 Moves so far = 0 Enter move : l4 D1 D2 D3 D4 ╔════╦════╦════╦════╗ R1 ║ 1 ║ 2 ║ 3 ║ 4 ║ L1 ╠════╬════╬════╬════╣ R2 ║ 7 ║ 8 ║ 5 ║ 6 ║ L2 ╠════╬════╬════╬════╣ R3 ║ 9 ║ 10 ║ 11 ║ 12 ║ L3 ╠════╬════╬════╬════╣ R4 ║ 13 ║ 14 ║ 15 ║ 16 ║ L4 ╚════╩════╩════╩════╝ U1 U2 U3 U4 Moves so far = 1 Enter move : l2 D1 D2 D3 D4 ╔════╦════╦════╦════╗ R1 ║ 1 ║ 2 ║ 3 ║ 4 ║ L1 ╠════╬════╬════╬════╣ R2 ║ 8 ║ 5 ║ 6 ║ 7 ║ L2 ╠════╬════╬════╬════╣ R3 ║ 9 ║ 10 ║ 11 ║ 12 ║ L3 ╠════╬════╬════╬════╣ R4 ║ 13 ║ 14 ║ 15 ║ 16 ║ L4 ╚════╩════╩════╩════╝ U1 U2 U3 U4 Moves so far = 2 Enter move : l2 D1 D2 D3 D4 ╔════╦════╦════╦════╗ R1 ║ 1 ║ 2 ║ 3 ║ 4 ║ L1 ╠════╬════╬════╬════╣ R2 ║ 5 ║ 6 ║ 7 ║ 8 ║ L2 ╠════╬════╬════╬════╣ R3 ║ 9 ║ 10 ║ 11 ║ 12 ║ L3 ╠════╬════╬════╬════╣ R4 ║ 13 ║ 14 ║ 15 ║ 16 ║ L4 ╚════╩════╩════╩════╝ U1 U2 U3 U4 Congratulations, you have won the game in 3 moves!!
JavaScript
Try it here.
You'll also need a html file:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono&display=swap" rel="stylesheet"> <link rel="stylesheet" type="text/css" media="screen" href="./css/main.css"> <title>16 Puzzle</title> </head> <body> <div id="done">WELL DONE!</div> <div id="board"></div> <div id="moves"></div> <button id="shuffle">SHUFFLE</button> <script src="./src/index.js" type="module"></script> </body> </html>
And css file:
* { margin: 0; border: 0; text-align: center; font-family: 'Ubuntu Mono', monospace; user-select: none; } button { border-radius: 5px; width: 300px; height: 80px; font-size: 40px; margin-top: 60px; } #board { width: 410px; height: 410px; margin: 120px auto 30px auto; } #done { font-size: 140px; padding: 20px; color: #fff; background-color: rgba(0, 23, 56, .5); border: 1px solid rgb(0, 90, 220); width: 700px; position: absolute; top: 250px; left: calc(50% - 380px); } #moves { font-size: 40px; line-height: 80px; height: 80px; width: 300px; margin: auto; border: 1px solid #000; border-radius: 5px; } .btn, .numbers, .hide { float: left; width: 64px; height: 64px; line-height: 65px; font-size: 40px; border: 1px solid black; color: black; background-color: white; cursor: none; margin: 1px; transition: all .3s; } .btn:hover { background-color: rgba(71, 231, 71, 0.5); cursor: pointer; } .hide { border: 1px solid white; cursor: none; }
<lang javascript> class Puzzle {
constructor() { this.moves; this.started;
this.board = document.getElementById("board"); document.getElementById("shuffle").addEventListener("click", () => { this.shuffle(); }); this.reset(); }
reset() { while (this.board.hasChildNodes()) { this.board.removeChild(this.board.firstChild); }
this.moves = 0; this.started = false; document.getElementById("moves").innerText = this.moves; document.getElementById("done").style.visibility = "hidden";
let t = 1; for (let y = 0; y < 6; y++) { for (let x = 0; x < 6; x++) { const d = document.createElement("div"); d.id = `${x}_${y}`; if (y === 0 || x === 0 || y === 5 || x === 5) { if (((y === 0 || y === 5) && (x === 0 || x === 5))) { d.className = "hide"; } else { d.className = "btn"; if (y === 0) { d.innerText = "🡇"; d.func = () => { this.rollDownRight(x, true); }; } else if (y === 5) { d.innerText = "🡅"; d.func = () => { this.rollUpLeft(x, true); }; } if (x === 0) { d.innerText = "🡆"; d.func = () => { this.rollDownRight(y, false); }; } else if (x === 5) { d.innerText = "🡄"; d.func = () => { this.rollUpLeft(y, false); }; } d.addEventListener("click", (ev) => { ev.srcElement.func(); }) } } else { d.className = "numbers"; d.innerText = `${t}`; t++; } this.board.appendChild(d); } } document.getElementById("shuffle").innerText = "SHUFFLE"; }
shuffle() { if (this.started) { this.reset(); } else { this.started = true; const e = Math.floor(Math.random() * 30) + 30; for (let z = 0; z < e; z++) { switch (Math.floor(Math.random() * 4)) { case 0: this.rollDownRight(Math.floor(Math.random() * 4) + 1, false); break; case 1: this.rollUpLeft(Math.floor(Math.random() * 4) + 1, true); break; case 2: this.rollUpLeft(Math.floor(Math.random() * 4) + 1, false); break; case 3: this.rollDownRight(Math.floor(Math.random() * 4) + 1, true); break; } } this.moves = 0; document.getElementById("moves").innerText = this.moves; document.getElementById("shuffle").innerText = "RESTART"; } }
getElements(l, col) { const z = Array.from(document.querySelectorAll(".numbers")); for (let e = 15; e > -1; e--) { if (z[e].id[(col ? 0 : 2)] != l) { z.splice(e, 1) } } return z; }
rollDownRight(x, col) { if (!this.started) return; const z = this.getElements(x, col), a = z[3].innerText; for (let r = 3; r > 0; r--) { z[r].innerText = z[r - 1].innerText; } z[0].innerText = a; this.updateMoves(); this.checkSolved(); }
rollUpLeft(x, col) { if (!this.started) return; const z = this.getElements(x, col), a = z[0].innerText; for (let r = 0; r < 3; r++) { z[r].innerText = z[r + 1].innerText; } z[3].innerText = a; this.updateMoves(); this.checkSolved(); }
updateMoves() { this.moves++; document.getElementById("moves").innerText = this.moves; }
checkSolved() { function check() { const z = document.querySelectorAll(".numbers"); let u = 1; for (let r of z) { if (r.innerText != u) return false; u++; } return true; } if (this.started && check()) { document.getElementById("done").style.visibility = "visible"; } }
}
new Puzzle(); </lang>
Julia
<lang julia>using Gtk, Random
function puzzle16app(bsize)
aclock, clock = "\u27f2", "\u27f3" win = GtkWindow("16 Game", 300, 300) |> (GtkFrame() |> (box = GtkBox(:v))) toolbar = GtkToolbar() newgame = GtkToolButton("New Game") set_gtk_property!(newgame, :label, "New Game") set_gtk_property!(newgame, :is_important, true) push!(toolbar, newgame) grid = GtkGrid() map(w -> push!(box, w),[toolbar, grid]) buttons = Array{Gtk.GtkButtonLeaf,2}(undef, bsize + 2, bsize + 2) for i in 1:bsize+2, j in 1:bsize+2 grid[i,j] = buttons[i,j] = GtkButton() set_gtk_property!(buttons[i,j], :expand, true) end
inorder = string.(reshape(1:bsize*bsize, bsize, bsize)) puzzle = shuffle(inorder) rotatecol(puzzle, col, n) = puzzle[:, col] .= circshift(puzzle[:, col], n) rotaterow(puzzle, row, n) = puzzle[row, :] .= circshift(puzzle[row, :], n) iswon() = puzzle == inorder won = false
function findrowcol(button) for i in 1:bsize+2, j in 1:bsize+2 if buttons[i, j] == button return i, j end end return 0, 0 end
function playerclicked(button) if !won i, j = findrowcol(button) if i == 1 rotatecol(puzzle, j - 1, 1) elseif i == bsize + 2 rotatecol(puzzle, j - 1, -1) elseif j == 1 rotaterow(puzzle, i - 1, 1) elseif j == bsize + 2 rotaterow(puzzle, i - 1, -1) end end update!() end
function setup!() for i in 1:bsize+2, j in 1:bsize+2 if 1 < j < bsize + 2 if i == 1 signal_connect(playerclicked, buttons[i, j], "clicked") elseif i == bsize + 2 signal_connect(playerclicked, buttons[i, j], "clicked") end elseif 1 < i < bsize + 2 if j == 1 signal_connect(playerclicked, buttons[i, j], "clicked") elseif j == bsize + 2 signal_connect(playerclicked, buttons[i, j], "clicked") end end end end
function update!() for i in 1:bsize+2, j in 1:bsize+2 if 1 < j < bsize + 2 if i == 1 set_gtk_property!(buttons[i, j], :label, clock) elseif i == bsize + 2 set_gtk_property!(buttons[i, j], :label, aclock) else set_gtk_property!(buttons[i, j], :label, puzzle[i-1, j-1]) end elseif 1 < i < bsize + 2 if j == 1 set_gtk_property!(buttons[i, j], :label, clock) elseif j == bsize + 2 set_gtk_property!(buttons[i, j], :label, aclock) end end end if iswon() won = true info_dialog("Game over.\nScore: $score", win) end showall(win) end
function initialize!(w) puzzle = shuffle(inorder) won = false update!() end setup!() condition = Condition() endit(w) = notify(condition) signal_connect(initialize!, newgame, :clicked) signal_connect(endit, win, :destroy) initialize!(win) showall(win) wait(condition)
end
puzzle16app(4) </lang>
Nim
Not a direct translation as there are a lot of differences, but, at least, the graphical interface is similar and the logic is the same. <lang Nim>import random, sequtils, strutils import gintro/[glib, gobject, gtk, gio]
const
BoardSize = 4 GridSize = BoardSize + 2 Clock = "\u27f2" AClock = "\u27f3"
type
Value = 1..16 Puzzle = array[BoardSize, array[BoardSize, Value]]
PuzzleApp = ref object of Application inOrder: Puzzle # Target grid. puzzle: Puzzle # Current grid. buttons: array[GridSize, array[GridSize, Button]] # Button grid. won: bool # True if game won. moves: Natural # Count of moves.
- ---------------------------------------------------------------------------------------------------
proc initPuzzle(puzzle: var Puzzle; data: openArray[Value]) =
## Initialize the puzzle with a list of values. var i = 0 for row in puzzle.mitems: for cell in row.mitems: cell = data[i] inc i
- ---------------------------------------------------------------------------------------------------
proc showMessage(app: PuzzleApp) =
## As "gintro" doesn't provide "MessageDialog" yet, we will use a simple dialog. let dialog = newDialog() dialog.setModal(true) let label = newLabel("You won in $# move(s)".format(app.moves)) dialog.contentArea.add(label) discard dialog.addButton("Ok", ord(ResponseType.ok)) dialog.showAll() discard dialog.run() dialog.destroy()
- ---------------------------------------------------------------------------------------------------
proc onQuit(button: ToolButton; window: ApplicationWindow) =
## Procedure called when clicking quit button. window.destroy()
- ---------------------------------------------------------------------------------------------------
proc rotateRow(puzzle: var Puzzle; row: Natural; left: bool) =
## Rotate a row left or right. if left: let first = puzzle[row][0] for i in 1..puzzle.high: puzzle[row][i-1] = puzzle[row][i] puzzle[row][^1] = first else: let last = puzzle[row][^1] for i in countdown(puzzle.high, 1): puzzle[row][i] = puzzle[row][i-1] puzzle[row][0] = last
- ---------------------------------------------------------------------------------------------------
proc rotateCol(puzzle: var Puzzle; col: Natural; up: bool) =
## Rotate a column up or down. if up: let first = puzzle[0][col] for i in 1..puzzle.high: puzzle[i-1][col] = puzzle[i][col] puzzle[^1][col] = first else: let last = puzzle[^1][col] for i in countdown(puzzle[0].high, 1): puzzle[i][col] =puzzle[i-1][col] puzzle[0][col] = last
- ---------------------------------------------------------------------------------------------------
proc findRowCol(app: PuzzleApp; button: Button): (int, int) =
## Find the row and column of a button. for i in [0, BoardSize+1]: for j in 1..Boardsize: if app.buttons[i][j] == button: return (i, j) for j in [0, BoardSize+1]: for i in 1..Boardsize: if app.buttons[i][j] == button: return (i, j)
- ---------------------------------------------------------------------------------------------------
proc update(app: PuzzleApp) =
## Update the grid. for i in 0..BoardSize+1: for j in 0..BoardSize+1: if j in 1..BoardSize: if i == 0: app.buttons[i][j].setLabel(Clock) elif i == BoardSize + 1: app.buttons[i][j].setLabel(AClock) else: app.buttons[i][j].setLabel($app.puzzle[i-1][j-1]) elif i in 1..BoardSize: if j == 0: app.buttons[i][j].setLabel(Clock) elif j == BoardSize + 1: app.buttons[i][j].setLabel(AClock)
if app.puzzle == app.inOrder: app.won = true app.showMessage()
- ---------------------------------------------------------------------------------------------------
proc onClick(button: Button; app: PuzzleApp) =
## Procedure called when the user cliked a grid button. if not app.won: inc app.moves let (i, j) = app.findRowCol(button) if i == 0: app.puzzle.rotateCol(j - 1, true) elif i == BoardSize + 1: app.puzzle.rotateCol(j - 1, false) elif j == 0: app.puzzle.rotateRow(i - 1, true) elif j == BoardSize + 1: app.puzzle.rotateRow(i - 1, false) app.update()
- ---------------------------------------------------------------------------------------------------
proc newGame(button: ToolButton; app: PuzzleApp) =
## Prepare a new game. var values = toSeq(Value.low..Value.high) values.shuffle() app.puzzle.initPuzzle(values) app.won = false app.update()
- ---------------------------------------------------------------------------------------------------
proc activate(app: PuzzleApp) =
## Activate the application.
let window = app.newApplicationWindow() window.setTitle("16 puzzle game") window.setSizeRequest(300, 340)
let box = newBox(Orientation.vertical, 0) window.add box
let toolbar = newToolbar() let newGameButton = newToolButton(label = "New game") toolbar.insert(newGameButton, 0) let quitButton = newToolButton(label = "Quit") toolbar.insert(quitButton, 1) box.add toolbar
let grid = newGrid() box.add grid
for i in 0..BoardSize+1: for j in 0..BoardSize+1: let button = newButton() button.setHexpand(true) button.setVexpand(true) app.buttons[i][j] = button grid.attach(button, j, i, 1, 1)
var values = toSeq(Value.low..Value.high) app.inOrder.initPuzzle(values) values.shuffle() app.puzzle.initPuzzle(values)
for i in [0, BoardSize + 1]: for j in 1..BoardSize: app.buttons[i][j].connect("clicked", onClick, app) for j in [0, BoardSize + 1]: for i in 1..BoardSize: app.buttons[i][j].connect("clicked", onClick, app)
newGameButton.connect("clicked", newGame, app) quitButton.connect("clicked", onQuit, window)
app.won = false app.update()
window.showAll()
- ———————————————————————————————————————————————————————————————————————————————————————————————————
randomize() let app = newApplication(PuzzleApp, "Rosetta.Puzzle16Game") discard app.connect("activate", activate) discard app.run()</lang>
Perl
<lang perl>#!/usr/bin/perl
use strict; # http://www.rosettacode.org/wiki/16_Puzzle_Game use warnings; use List::Util qw( any ); use Tk;
my $size = $ARGV[0] // 4; my $width = length $size ** 2; my $message = ; my $steps; my @board; my @again; my $easy = 3;
my $mw = MainWindow->new( -title => '16 Puzzle in Tk' ); $mw->geometry( '+470+300' ); $mw->optionAdd('*font' => 'sans 14'); my $frame = $mw->Frame(-bg => 'gray', -relief => 'ridge',
-borderwidth => 5)->pack;
my $label = $mw->Label( -textvariable => \$message, -font => 'times-bold 30',
)->pack;
$mw->Button( -text => "Exit", -command => sub {$mw->destroy},
)->pack(-side => 'right');
$mw->Button( -text => "Reset", -command => sub {
@board = @again; show(); $message = $steps = 0; $label->configure(-fg => 'black'); },)->pack(-side => 'right');
$mw->Button( -text => "Easy", -command => sub {$easy = 3; generate()},
)->pack(-side => 'left');
$mw->Button( -text => "Hard", -command => sub {$easy = 12; generate()},
)->pack(-side => 'left');
my @cells = map {
$frame->Label(-text => $_, -relief => 'sunken', -borderwidth => 2, -fg => 'white', -bg => 'blue', -font => 'sans 24', -padx => 7, -pady => 7, -width => $width, )->grid(-row => int( $_ / $size + 2 ), -column => $_ % $size + 2, -sticky => "nsew", ) } 0 .. $size ** 2 - 1;
for my $i (1 .. $size)
{ $frame->Button(-text => ">", -command => sub {move("R$i") }, )->grid(-row => $i + 1, -column => 1, -sticky => "nsew"); $frame->Button(-text => "<", -command => sub {move("L$i") }, )->grid(-row => $i + 1, -column => $size + 2, -sticky => "nsew"); $frame->Button(-text => "v", -command => sub {move("D$i") }, )->grid(-row => 1, -column => $i + 1, -sticky => "nsew"); $frame->Button(-text => "^", -command => sub {move("U$i") }, )->grid(-row => $size + 2, -column => $i + 1, -sticky => "nsew"); }
generate();
MainLoop; -M $0 < 0 and exec $0, @ARGV; # restart if source file modified since start
sub randommove { move( qw(U D L R)[rand 4] . int 1 + rand $size ) }
sub show { $cells[$_]->configure(-text => $board[$_]) for 0 .. $size ** 2 - 1 }
sub success { not any { $_ + 1 != $board[$_] } 0 .. $size ** 2 - 1 }
sub move
{ my ($dir, $index) = split //, shift; $index--; my @from = map { $dir =~ /L|R/i ? $_ + $size * $index : $_ * $size + $index } 0 .. $size - 1; @board[@from] = (@board[@from])[ ($dir =~ /L|U/i || -1) .. $size - 1, 0 ]; show(); $message = ++$steps; $label->configure(-fg => success() ? 'red' : 'black'); success() and $message = "Solved in $steps"; }
sub generate
{ @board = 1 .. $size ** 2; randommove() for 1 .. 1 + int rand $easy; success() and randommove(); @again = @board; $message = $steps = 0; }</lang>
Phix
NB arrow keys not tested on linux, but "UDLR" should work...
constant level = 5, ESC=27, UP=328, DOWN=336, LEFT=331, RIGHT=333 sequence board = tagset(16), solve = board procedure print_board() printf(1," 1 2 3 4\n") for r=1 to 4 do printf(1,"%d: %2d %2d %2d %2d\n",r&board[r*4-3..r*4]) end for puts(1,"\n") end procedure procedure move(integer d,rc) -- d is 1..4 for up/down/left/right -- rc is 1..4 for row(d>=3)/column(d<=2) sequence idx = repeat(0,4), tiles = repeat(0,4) for i=1 to 4 do idx[i] = iff(d<=2?rc+(i-1)*4:(rc-1)*4+i) tiles[i] = board[idx[i]] end for -- ?{d,rc,idx} idx = iff(mod(d,2)?idx[4]&idx[1..3]:idx[2..4]&idx[1]) for i=1 to 4 do board[idx[i]] = tiles[i] end for end procedure for i=1 to level do move(rand(4),rand(4)) end for while 1 do print_board() if board=solve then puts(1,"Solved!\n") exit end if puts(1,"Your move (escape|Up/Down|Left/Right & 1/2/3/4):") integer d, rc while true do while true do d = find(upper(wait_key()),{ESC,UP,DOWN,LEFT,RIGHT}&"UDLR")-1 if d!=-1 then exit end if end while if d=0 then puts(1,"\n\nYou gave up!\n") exit end if if d>4 then d-=4 end if puts(1,"UDLR"[d]) while true do rc = find(upper(wait_key()),ESC&"1234UDLR"&{UP,DOWN,LEFT,RIGHT}