15 puzzle game: Difference between revisions

m
Back to previous version, because of better understanding.
m (→‎{{header|68000 Assembly}}: improved readability)
m (Back to previous version, because of better understanding.)
Tag: Manual revert
 
(68 intermediate revisions by 20 users not shown)
Line 27:
{{trans|Python: Original, with output}}
 
<langsyntaxhighlight lang="11l">T Puzzle
position = 0
[Int = String] items
Line 140:
I g.game_over()
print(‘You WON’)
L.break</langsyntaxhighlight>
 
{{out}}
The same as in Python.
 
 
=={{header|68000 Assembly}}==
This is an entire Sega Genesis game, tested in the Fusion emulator. Thanks to Keith S. of Chibiakumas for the cartridge header, font routines, and printing logic. I programmed the actual game logic. This code can be copied and pasted into a text file and assembled as-is using vasmm68k_mot_win32.exe, no includes or incbins necessary (even the bitmap font is here too.)
 
<langsyntaxhighlight lang="68000devpac">;15 PUZZLE GAME
;Ram Variables
Cursor_X equ $00FF0000 ;Ram for Cursor Xpos
Cursor_Y equ $00FF0000+1 ;Ram for Cursor Ypos
joypad1 equ $00FF0002
joypad2 equ $00FF0004
 
GameRam equ $00FF1000 ;Ram for where the pieces are
Line 806 ⟶ 804:
DC.B $80 ;23 DMA source address high (C=CMD) CCHHHHHH
VDPSettingsEnd:
even</langsyntaxhighlight>
 
{{out}}[https://ibb.co/4MZpL4W Screenshot of emulator]
Line 812 ⟶ 810:
=={{header|AArch64 Assembly}}==
{{works with|as|Raspberry Pi 3B version Buster 64 bits}}
<syntaxhighlight lang="aarch64 assembly">
<lang AArch64 Assembly>
/* ARM assembly AARCH64 Raspberry PI 3B */
/* program puzzle15_64.s */
Line 1,433 ⟶ 1,431:
/* for this file see task include a file in language AArch64 assembly */
.include "../includeARM64.inc"
</syntaxhighlight>
</lang>
 
=={{header|Action!}}==
<langsyntaxhighlight Actionlang="action!">DEFINE BOARDSIZE="16"
DEFINE X0="13"
DEFINE Y0="6"
Line 1,649 ⟶ 1,647:
FI
OD
RETURN</langsyntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/15_puzzle_game.png Screenshot from Atari 8-bit computer]
Line 1,657 ⟶ 1,655:
We fist define a generic package Generic_Puzzle. Upon instantiation, it can take any number of rows, any number of columns for a rows*columns-1 game. Instead of plain numbers, the tiles on the board can have arbitrary names (but they should all be of the same length). The package user can request the name for the tile at a certain (row,column)-point, and the set of possible moves. The user can move the empty space up, down, left and right (if possible). If the user makes the attempt to perform an impossible move, a Constraint_Error is raised.
 
<langsyntaxhighlight Adalang="ada">generic
Rows, Cols: Positive;
with function Name(N: Natural) return String; -- with Pre => (N < Rows*Cols);
Line 1,672 ⟶ 1,670:
procedure Move(The_Move: Moves);
 
end Generic_Puzzle;</langsyntaxhighlight>
 
The package implementation is as follows.
 
<langsyntaxhighlight Adalang="ada">package body Generic_Puzzle is
Field: array(Row_Type, Col_Type) of Natural;
Line 1,732 ⟶ 1,730:
end loop;
end;
end Generic_Puzzle;</langsyntaxhighlight>
 
The main program reads the level from the command line. A larger level implies a more difficult instance. The default level is 10, which is fairly simple. After randomizing the board, the user can move the tiles.
 
<langsyntaxhighlight Adalang="ada">with Generic_Puzzle, Ada.Text_IO,
Ada.Numerics.Discrete_Random, Ada.Command_Line;
 
Line 1,807 ⟶ 1,805:
end;
end loop;
end Puzzle_15;</langsyntaxhighlight>
 
{{out}}
Line 1,849 ⟶ 1,847:
4 moves!</pre>
 
For other puzzles, one must just the single line with the package instantiation. E.g., for an 8-puzzle, we would write the following. <langsyntaxhighlight Adalang="ada"> package Puzzle is new Generic_Puzzle(Rows => 3, Cols => 3, Name => Image);</langsyntaxhighlight>
 
=={{header|Amazing Hopper}}==
<syntaxhighlight lang="Amazing Hopper">
#include <jambo.h>
 
#define FILATABLA 5
#define COLUMNATABLA 10
#define Imprimelamatriz Gosub 'Pone la matriz'
#define Imprimelascasillas Gosub 'Pone las casillas'
#define Imprimeelíndiceen(_X_,_Y_) Set '_X_,_Y_', Gosub 'Pone el índice'
 
Main
Set break
 
Void (casilla, índice, números)
Link gosub( Crea una casilla, Crea el índice, Crea la matriz de números )
Cls
x=4, y=4, Tok sep '""', Gosub 'Imprime escenario'
/* INICIA EL JUEGO */
SW = 1, GANADOR = 0
c=0, cero x=4, cero y=4
Loop
Let ( c:=Getch )
Switch ( c )
Case 'KRIGHT' { #( y < 4 ) do{ ++y }, Exit }
Case 'KDOWN' { #( x < 4 ) do{ ++x }, Exit }
Case 'KLEFT' { #( y > 1 ) do{ --y }, Exit }
Case 'KUP' { #( x > 1 ) do{ --x }, Exit }
Case 'KRETURN' { If ( Gosub 'Chequear si movimiento es válido' )
Gosub 'Mover las casillas'
End If
Exit
}
Case 'KESCAPE' { SW=0 }
End switch
Gosub 'Imprime escenario'
Break if ( Gosub 'Verificar puzzle resuelto' --- Backup to 'GANADOR' )
Back if 'SW' is not zero
 
/* FIN DEL JUEGO */
If ( GANADOR )
Locate (18,15), Printnl("LO RESOLVISTE!")
End If
Locate (19,1), Prnl
End
 
Subrutines
 
/* CHEQUEO DE MOVIMIENTO */
 
Define ( Verificar puzzle resuelto )
ret = 0
Clr all marks
Tnúmeros=números
Redim (Tnúmeros,0), N = 0, Let ( N := Length(Tnúmeros) Minus (1))
i=1
Iterator ( ++i, Less equal ( i, N ) And( Not(ret) ), \
Let ( ret := Bit xor(i, [i] Get 'Tnúmeros') ) )
Clr all marks
Clear 'Tnúmeros'
 
Return 'Not (ret); And( Equals(i, Plus one(N)) ) '
 
Define ( Chequear si movimiento es válido )
Return 'Only one ( Equals (x, cero x), Equals(y, cero y ) )'
 
Define ( Mover las casillas )
If ( Equals (y, cero y) )
If ( Less (x, cero x) ) // mueve hacia abajo
Loop for ( i = cero x, #( i >= x ) , --i )
If ( Greater ( i, 1 ) )
[{i} Minus(1), y] Get 'números', [i,y] Put 'números'
Else
[{i} Plus(1), y] Get 'números', [i,y] Put 'números'
End If
Next
Else // por defecto: mueve hacia arriba
Loop for ( i = cero x, #( i <= x ) , ++i )
If ( Less ( i, 4 ) )
[{i} Plus(1), y] Get 'números', [i,y] Put 'números'
Else
[i,y] Get 'números', [{i} Minus(1),y] Put 'números'
End If
Next
End If
[x,y] Set '0', Put 'números'
Set 'x', Move to 'cero x'
Else // por defecto: está en la misma fila
If ( Less ( y, cero y ) ) // mueve hacia la derecha
Loop for ( i = cero y, #( i >= y ) , --i )
If ( Greater ( i, 1) )
[x, {i} Minus(1)] Get 'números', [x,i] Put 'números'
Else
[x, y] Get 'números', [x, {i} Plus(1)] Put 'números'
End If
Next
Else // por defecto: mueve hacia la izquierda
Loop for ( i = cero y, #( i <= y ) , ++i )
If ( Less ( i, 4 ) )
[x, {i} Plus(1)] Get 'números', [x,i] Put 'números'
Else
[x,i] Get 'números', [x,{i} Minus(1)] Put 'números'
End If
Next
End If
[x,y] Set '0', Put 'números'
Set 'y', Move to 'cero y'
End If
Clr all marks
Return
 
/* DESPLIEGUE DE CUADRITOS Y NUMEROS */
 
Define ( Imprime escenario )
Imprime las casillas
Imprime el índice en 'x,y'
Imprime la matriz
Return
 
Define ( Pone la matriz )
i=4, col = COLUMNA TABLA, celda=""
Clr all marks
py=1
Loop
j=4, fil = FILA TABLA, px=1
Loop
Locate 'Plus one(fil), Plus two (col)'
Printnl( Get if ([px,py] Get 'números' ---Backup to (celda)---, celda, " ") )
fil += 3
--j, ++px
Back if (j) is not zero
col += 6, --i, ++py
Back if (i) is not zero
Return
 
Define ( Pone las casillas )
i=4, col = COLUMNA TABLA
Clr all marks
Loop
j=4, fil = FILA TABLA
Loop
Set 'fil, col', Gosub 'Pone un cuadrito'
fil += 3, --j
Back if (j) is not zero
col += 6, --i
Back if (i) is not zero
Return
 
Define (Pone un cuadrito, fil, col)
Locate 'fil, col', Print table 'casilla'
Return
 
Define ( Pone el índice, fil, col )
/* 5+(fil-1)*3 fila
10+(col-1)*6 col */
Clr all marks
Locate 'Minus one(fil) Mul by (3) Plus (FILA TABLA), Minus one(col) Mulby(6) Plus(COLUMNA TABLA)'
Print table 'índice'
Return
 
/* CONFIGURACION DEL JUEGO */
 
Define ( Crea la matriz de números )
Sequence ( 0, 1, 16, números )
Gosub 'Barajar el array'
Redim ( números, 4,4 )
Return
 
/* algoritmo de Fisher-Yates */
Define ( Barajar el array )
N = 0, Let ( N := Length(números) )
R = 0, aux = 0
Loop
Let (R := Ceil(Rand(N)))
Let (aux := [R] Get 'números' )
[N] Get 'números', [R] Put 'números'
Set 'aux', [N] Put 'números'
--N
Back if 'N' is positive
If ( [16] Get 'números' ---Backup to 'aux'---, Not (Is zero?) )
[aScan(1,0,números)] Set 'aux', Put 'números'
[16] Set '0', Put 'números'
End If
Return
 
Define ( Crea una casilla )
Set 'Utf8(Chr(218)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(191))', Apnd row 'casilla'
Set 'Utf8(Chr(179))," "," "," "," ",Utf8(Chr(179))', Apnd row 'casilla'
Set 'Utf8(Chr(192)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(196)),Utf8(Chr(217))', Apnd row 'casilla'
Return
 
Define ( Crea el índice )
Set 'Utf8(Chr(220)),Utf8(Chr(220)),Utf8(Chr(220)),Utf8(Chr(220)),Utf8(Chr(220)),Utf8(Chr(220))', Apnd row 'índice'
Set 'Utf8(Chr(219))," "," "," "," ",Utf8(Chr(219))', Apnd row 'índice'
Set 'Utf8(Chr(223)),Utf8(Chr(223)),Utf8(Chr(223)),Utf8(Chr(223)),Utf8(Chr(223)),Utf8(Chr(223))', Apnd row 'índice'
Return
 
</syntaxhighlight>
{{out}}
<pre>
$ hopper jm/puzzle.jambo
┌────┐┌────┐┌────┐┌────┐
│ 14 ││ 5 ││ 3 ││ 12 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 13 ││ 9 ││ 6 ││ 11 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 15 ││ 10 ││ 8 ││ 2 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐▄▄▄▄▄▄
│ 4 ││ 1 ││ 7 │█ █
└────┘└────┘└────┘▀▀▀▀▀▀
..... (muchos click)
┌────┐┌────┐┌────┐┌────┐
│ 7 ││ 10 ││ 14 ││ 12 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 15 ││ ││ 4 ││ 2 │
└────┘└────┘└────┘└────┘
┌────┐▄▄▄▄▄▄┌────┐┌────┐
│ 1 │█ 8 █│ 6 ││ 11 │
└────┘▀▀▀▀▀▀└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 3 ││ 13 ││ 5 ││ 9 │
└────┘└────┘└────┘└────┘
....( muy muchos clicks )
┌────┐┌────┐┌────┐┌────┐
│ 1 ││ 2 ││ 3 ││ 4 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 5 ││ 6 ││ 7 ││ 8 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐┌────┐
│ 9 ││ 10 ││ 11 ││ 12 │
└────┘└────┘└────┘└────┘
┌────┐┌────┐┌────┐▄▄▄▄▄▄
│ 13 ││ 14 ││ 15 │█ █
└────┘└────┘└────┘▀▀▀▀▀▀
 
LO RESOLVISTE!
</pre>
 
=={{header|APL}}==
{{works with|Dyalog APL|16.0}}
<langsyntaxhighlight APLlang="apl">fpg←{⎕IO←0
⍺←4 4
(s∨.<0)∨2≠⍴s←⍺:'invalid shape:'s
Line 1,877 ⟶ 2,121:
(1↓⍺)∇ n
}z
}</langsyntaxhighlight>
{{out}}
<pre> fpg 10
Line 1,926 ⟶ 2,170:
=={{header|ARM Assembly}}==
{{works with|as|Raspberry Pi}}
<syntaxhighlight lang="arm assembly">
<lang ARM Assembly>
 
/* ARM assembly Raspberry PI */
Line 2,564 ⟶ 2,808:
bx lr @return
 
</syntaxhighlight>
</lang>
 
=={{header|Arturo}}==
<syntaxhighlight lang="arturo">
;; ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
;; ===>> ~~ Game's functions ~~ <<===
;; --->> ~~ Init functions ~~ <<---
 
;; This is a solved sample that is used to
;; init and finish the game
solvedTable: @[ " 1 " " 2 " " 3 " " 4 "
" 5 " " 6 " " 7 " " 8 "
" 9 " " 10 " " 11 " " 12 "
" 13 " " 14 " " 15 " " " ]
 
;; Use this once in :game's init, to get a player position
;; Q: Why use it once?
;; A: This algorithm is slower than just get a stored varible
;; yet this searches for a string for every value from :game
getPlayerPosition: $[table :block][
return index table " "
]
 
;; This is the object that represents the game
;; 'table » The sample table to generate the game
define :game [
table :block
][
 
init: [
; checks if 'table has 16 elements
ensure [16 = size this\table]
 
;; The game's table itself
this\table: (shuffle this\table) ; creates a random game
;; The current movement. When less, better is your punctuation
this\movements: 0
;; The current 'playerPosition in table
;; Used to evaluate if certain movement is possible or not
this\playerPosition: getPlayerPosition this\table
;; Defines it the gameLoop still running
this\running?: true
]
 
;; A builtin print function that simplifies the use
print: [
render {
Movements: |this\movements|, Position: |this\playerPosition|
*-----*-----*-----*-----*
|this\table\0| |this\table\1| |this\table\2| |this\table\3|
*-----*-----*-----*-----*
|this\table\4| |this\table\5| |this\table\6| |this\table\7|
*-----*-----*-----*-----*
|this\table\8| |this\table\9| |this\table\10| |this\table\11|
*-----*-----*-----*-----*
|this\table\12| |this\table\13| |this\table\14| |this\table\15|
*-----*-----*-----*-----*
}
]
 
;; Compares the internal's 'table with another :block
compare: [
if this\table = that
-> return true
]
 
]
 
;; These are the commands used internally on game
;; To avoid ambiguity, User's input'll to be translated to this
gameActions: ['up, 'left, 'down, 'right, 'quit]
 
 
;; ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
;; -->> Print funnctions <<---
 
;; A template for print instructions
printInstructions: [
color #cyan "Type (WASD) to move and (Q) to quit."
]
 
;; A template for print input warning
;; 'input: the wrong input itself that will be printed
printWrongInput: $[inp :string][
print color #red
~"Wrong input: '|inp|'"
]
 
;; A template for print input warning
;; 'action: could be 'up, 'down, 'left or 'right
printWrongMovement: $[action :literal][
print color #red
~"Wrong movement. Can't go |action|"
]
 
 
;; ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
;; --->> Validators/Checkers functions <<---
 
;; Checks if a 'input is in 'gameActions
;; Valids for: 'up, 'down, 'left, 'right and 'quit
validInput?: $[inp :any][
return (in? inp gameActions)
]
 
;; Checks if the current movement tried is possible
;; 'game » is the current game
;; 'movement » must be in 'gameActions, but can't be 'quit
validMovement?: $[
game :game
movement :literal
][
pos: game\playerPosition
case [movement]
when? [='up]
-> return (not? in? pos [0..3])
when? [='down]
-> return (not? in? pos [12..15])
when? [='left]
-> return (not? in? pos [0 4 8 12])
when? [='right]
-> return (not? in? pos [3 7 11 15])
else
-> return false
]
 
 
;; ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
;; --->> Action functions <<---
 
;; Gets user input from terminal
;; returning a :literal from 'gameActions
;; Raises: In case of wrong input,
;; will be returned the same input as a :string
parseInput: $[inp :string][
lowerInp: lower inp
case [lowerInp]
when? [="w"] -> return 'up
when? [="a"] -> return 'left
when? [="s"] -> return 'down
when? [="d"] -> return 'right
when? [="q"] -> return 'quit
else -> return inp
]
 
;; Moves the player in Game's Table
;; Note that this's a unsafe function,
;; use 'validMovement? to check a 'movement given a game,
;; and then use this
movePlayer: $[
game :game
movement :literal
][
 
position: game\playerPosition
 
updateGame: $[
game :game
playerPosition :integer
relativePosition :integer
][
try [
 
; 'otherPosition is the real index of the 'relativePosition
otherPosition: + playerPosition relativePosition
 
; -- Updates the table, swaping the positions
temp: game\table\[playerPosition]
game\table\[playerPosition]: game\table\[otherPosition]
game\table\[otherPosition]: temp
 
; -- Updates player's status
game\playerPosition: otherPosition
game\movements: inc game\movements
] else -> panic "'movement didn't checked."
]
 
case [movement]
when? [='up]
-> (updateGame game position (neg 4))
when? [='down]
-> (updateGame game position (4))
when? [='left]
-> (updateGame game position (neg 1))
when? [='right]
-> (updateGame game position (1))
else -> panic "'movement didn't checked."
 
]
 
endGame: $[
message :string
][
print message
exit
]
 
 
;; ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~ ~~
;; --->> Run function <<---
 
;; Inits ans runs the game
;; 'sampleTable must be already solved
runGame: $[sampleTable :block][
game: to :game [sampleTable]
 
while [game\running?] [
print game
print printInstructions
command: parseInput input ">> "
 
if command = 'quit
-> endGame "Exiting game..."
 
validInp: validInput? command
if? validInp [
validMov: validMovement? game command
(validMov)?
-> movePlayer game command
-> printWrongMovement command
] else
-> printWrongInput command
 
if sampleTable = game
-> endGame "Congratulations! You won!"
print ""
]
]
 
 
runGame solvedTable</syntaxhighlight>
 
=={{header|Astro}}==
<langsyntaxhighlight lang="python">type Puzzle(var items: {}, var position: -1)
 
fun mainframe(puz):
Line 2,681 ⟶ 3,155:
print 'You WON'
break
</syntaxhighlight>
</lang>
 
=={{header|AutoHotkey}}==
<langsyntaxhighlight AutoHotkeylang="autohotkey">Size := 20
Grid := [], Deltas := ["-1,0","1,0","0,-1","0,1"], Width := Size * 2.5
Gui, font, S%Size%
Line 2,760 ⟶ 3,234:
if (Trim(gridCont, ",") = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16")
MsgBox, 262208, 15 Puzzle, You solved 15 Puzzle
}</langsyntaxhighlight>
 
=={{header|BASIC}}==
==={{header|Applesoft BASIC}}===
<syntaxhighlight lang="basic"> 100 GOSUB 500INITIALIZE
110 FOR Q = 1 TO 1
120 IF I <> X OR J <> Y THEN GOSUB 200MOVE
130 ON W GOSUB 330,450
140 LET I = K(0, K) + X
150 LET J = K(1, K) + Y
160 LET Q = K(2, K) OR W = 3
170 NEXT Q
180 VTAB T + 3
190 END
 
REM MOVE
200 IF I < 0 THEN RETURN
210 IF I > 3 THEN RETURN
220 IF J < 0 THEN RETURN
230 IF J > 3 THEN RETURN
240 LET M = (I + J * 4) * 3
250 LET N = (X + Y * 4) * 3
260 IF N > M GOTO 290SWAP
270 LET N = M
280 LET M = (X + Y * 4) * 3
REM SWAP
290 LET A$ = MID$(A$, 1, M) + MID$(A$, N + 1, 2) + MID$(A$,M + 3, N - M - 2) + MID$(A$, M + 1, 2) + MID$(A$, N + 3)
300 LET X = I
310 LET Y = J
320 ON W GOTO 440,400
 
REM RANDOM MOVE
330 VTAB T + 3
340 HTAB 2
350 PRINT MID$(S$, S + 1, 10);
360 LET S = NOT S
370 LET K = INT(RND(1) * 4) + 1
380 IF PEEK(49152) < 128 OR A$ = W$ THEN RETURN
390 LET K = PEEK(49168) * 0
REM SHOW
400 VTAB T
410 HTAB 1
420 PRINT A$;
430 LET W = (A$ = W$) + 2
REM DON'T SHOW
440 RETURN
 
REM GET KEY
450 VTAB T + Y
460 HTAB X * 3 + 2
470 GET K$
480 LET K = ASC (K$)
490 RETURN
 
REM INITIALIZE
500 PRINT " 15-PUZZLE"
 
REM KEYBOARD
 
REM ARROW KEYS TWO HANDED CLASSIC T REVERSE T SEQUENCED
REM ^K A I G ^C
REM ^H ^J ^U , Z . J K L H T F ^B ^D ^A
 
REM RIGHT , J H ^A
510 DATA8,44,74,106,72,104,1
 
REM LEFT . L F ^B
520 DATA21,46,76,108,70,102,2
 
REM DOWN A I G ^C
530 DATA11,65,97,73,105,71,103,3
 
REM UP Z K T ^D
540 DATA10,90,122,75,107,84,116,4
 
REM QUIT ^Q ESC
550 DATA0,17,27,0
560 DIM K(2,127)
570 FOR V = 0 TO 2
580 FOR D = - 1 TO 1 STEP 2
590 FOR R = 1 TO 1
600 READ K
610 LET K(V,K) = D
620 LET R = K < 5
630 NEXT R,D,V
640 LET A$ = " 1 2 3 4"
650 LET M$ = CHR$ (13)
660 LET L$ = " 5 6 7 8"
670 LET A$ = A$ + M$ + L$
680 LET L$ = " 9 10 11 12"
690 LET A$ = A$ + M$ + L$
700 LET L$ = "13 14 15 "
710 LET A$ = A$ + M$ + L$
720 LET W$ = A$
730 DATA3,3,3,3,1,0
740 READ X,Y,I,J,W,k(2, 0)
750 PRINT "PRESS A KEY"
760 PRINT " TO STOP"
770 LET S$ = " SHUFFLING "
780 LET T = PEEK(37) - 2
790 RETURN</syntaxhighlight>
==={{header|Commodore BASIC}}===
<langsyntaxhighlight lang="basic">10 REM 15-PUZZLE GAME
20 REM COMMODORE BASIC 2.0
30 REM ********************************
Line 2,879 ⟶ 3,451:
1130 FOR T=0 TO 400
1140 NEXT
1150 RETURN</langsyntaxhighlight>
 
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}{{works with|ARM BBC BASIC}}{{works with|Brandy BASIC|Matrix Brandy}}
<langsyntaxhighlight lang="bbcbasic"> IF INKEY(-256)=77 OR (INKEY(-256) AND &F0)=&A0 THEN MODE 1: COLOUR 0: COLOUR 143: *FX4,1
 
SIZE=4 : DIFFICULTY=3
Line 2,927 ⟶ 3,499:
COLOUR 0 : COLOUR 143
PRINT
ENDPROC</langsyntaxhighlight>
 
=={{header|BQN}}==
{{trans|APL}}
<syntaxhighlight lang="bqn">_while_ ← {𝔽⍟𝔾∘𝔽_𝕣_𝔾∘𝔽⍟𝔾𝕩}
FPG←{
𝕊𝕩: 4‿4𝕊𝕩;
(∧´𝕨<0)∨2≠≠𝕨 ? •Out "Invalid shape: "∾•Fmt 𝕨;
0≠=𝕩 ? •Out "Invalid shuffle count: "∾•Fmt 𝕩;
s𝕊𝕩:
d←⟨1‿0⋄¯1‿0⋄0‿1⋄0‿¯1⟩ # Directions
w←𝕨⥊1⌽↕×´𝕨 # Solved grid
b←w # Board
z←⊑{
z‿p←𝕩
p↩(⊢≡s⊸|)¨⊸/(<z)+d(¬∘∊/⊣)p # filter out invalid
n←(•rand.Range ≠p)⊑p
b⌽⌾(z‿n⊸⊑)↩ # switch places
-`n‿z
}⍟𝕩 ⟨𝕨-1,⟨0⟩⟩
{
𝕊:
b≡w ? •Show b, •Out "You win", 0;
•Show b
inp←⊑{
Check 𝕩:
•Out "Enter move: "
x←•GetLine@
i←⊑"↑↓←→q"⊐x
{
i=4 ? i; # quit
i>4 ? •Out "Invalid direction: "∾x, Check x;
(⊢≢s⊸|)z+i⊑d ? •Out "Out of bounds: "∾x, Check x;
i
}
} @
{
𝕩=4 ? •Out "Quitting", 0;
mv←z+𝕩⊑d
b⌽⌾(mv‿z⊸⊑)↩
z↩mv
1
} inp
} _while_ ⊢ 1
@
}</syntaxhighlight>
<syntaxhighlight lang="bqn"> )ex 15_puzzle.bqn
FPG 10
┌─
╵ 1 2 0 3
5 6 7 4
9 10 11 8
13 14 15 12
Enter move:
a
Invalid direction: a
Enter move:
┌─
╵ 1 2 7 3
5 6 0 4
9 10 11 8
13 14 15 12
Enter move:
┌─
╵ 1 2 0 3
5 6 7 4
9 10 11 8
13 14 15 12
Enter move:
Out of bounds: ↓
...</syntaxhighlight>
 
=={{header|C}}==
===C89, 22 lines version===
The task, as you can see, can be resolved in 22 lines of no more than 80 characters. Of course, the source code in C is not very readable. The second example works exactly the same way, but it was written in much more human readable way. The program also works correctly for non-standard number of rows and/or columns.
<langsyntaxhighlight Clang="c">/* RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC, § 22 */
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
Line 2,953 ⟶ 3,601:
RIGHT;}}}void pause(void){getchar();}int main(void){srand((unsigned)time(NULL));
do setup();while(isEnd());show();while(!isEnd()){update(get());show();}disp(
"You win"); pause();return 0;}</langsyntaxhighlight>
 
===C89, short version, TTY mode===
<syntaxhighlight lang="c">/*
<lang C>/*
* RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC
*/
Line 3,079 ⟶ 3,727:
}
 
</syntaxhighlight>
</lang>
{{Out}}
<pre>
Line 3,111 ⟶ 3,759:
 
===C89, long version, TTY/Winapi/ncurses modes===
<langsyntaxhighlight Clang="c">/**
* RosettaCode: Fifteen puzle game, C89, MS Windows Console API, MVC
*
Line 3,421 ⟶ 4,069:
return EXIT_SUCCESS;
}
</syntaxhighlight>
</lang>
 
=={{header|C sharp|C#}}==
{{libheader|System.Windows.Forms}}
{{libheader|System.Drawing}}
{{works with|C sharp|63+}}
<langsyntaxhighlight lang="csharp">using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
 
public class FifteenPuzzle
{
const int gridSizeGridSize = 4; //Standard 15 puzzle is 4x4
const boolint evenSizedBlockCount = gridSize % 2 == 016;
const int blockCount = gridSize * gridSize;
const int last = blockCount - 1;
const int buttonSize = 50;
const int buttonMargin = 3; //default = 3
const int formEdge = 9;
static readonly Random rnd = new Random();
static readonly Font buttonFont = new Font("Arial", 15.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0)));
readonly Button[] buttons = new Button[blockCount];
readonly int[] grid = new int[blockCount];
readonly int[] positionOf = new int[blockCount];
int moves = 0;
DateTime start;
 
public static voidreadonly Main(string[]Random argsR = new Random();
 
private List<Button> Puzzles = new List<Button>();
private int Moves = 0;
private DateTime Start;
 
public class Puzzle
{
FifteenPuzzleprivate pint = new FifteenPuzzle()mOrderedNumer;
 
Form f = p.BuildForm();
Application.Run(f)public int CurrentNumber;
 
public int X;
public int Y;
 
public int InvX
{
get { return (GridSize - 1) - X; }
}
public int InvY
{
get { return (GridSize - 1) - Y; }
}
 
public Puzzle(int OrderedNumer)
{
mOrderedNumer = OrderedNumer;
 
CurrentNumber = OrderedNumer;
 
X = OrderedNumer % GridSize;
Y = OrderedNumer / GridSize;
}
public Puzzle(int OrderedNumer, int CurrentNumber)
: this(OrderedNumer)
{
this.CurrentNumber = CurrentNumber;
}
 
public bool IsEmptyPuzzle
{
get { return CurrentNumber >= (BlockCount - 1); }
}
public bool IsTruePlace
{
get { return (CurrentNumber == mOrderedNumer); }
}
public bool NearestWith(Puzzle OtherPz)
{
int dx = (X - OtherPz.X);
int dy = (Y - OtherPz.Y);
 
if ((dx == 0) && (dy <= 1) && (dy >= -1)) return true;
if ((dy == 0) && (dx <= 1) && (dx >= -1)) return true;
 
return false;
}
 
public override string ToString()
{
return (CurrentNumber + 1).ToString();
}
}
 
public FifteenPuzzlestatic void Main(string[] args)
{
forFifteenPuzzle (int iGame = 0;new i < blockCountFifteenPuzzle(); i++) {
grid[i] = iApplication.Run(Game.CreateForm());
positionOf[i] = i;
}
}
 
private Form BuildFormCreateForm()
{
Buttonint startButtonButtonSize = new Button {50;
int ButtonMargin = 3;
Font = new Font("Arial", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))),
int SizeFormEdge = new Size(86, 23),9;
Location = new Point(formEdge,
(buttonSize + buttonMargin * 2) * gridSize + buttonMargin + formEdge),
Text = "New Game",
UseVisualStyleBackColor = true
};
startButton.Click += (sender, e) => Shuffle();
 
Font ButtonFont = new Font("Arial", 15.75F, FontStyle.Regular);
int size = buttonSize * gridSize + buttonMargin * gridSize * 2 + formEdge * 2;
 
Form form = new Form {
Button StartButton = new Text = "Fifteen",Button();
ClientSizeStartButton.Location = new SizePoint(width: sizeFormEdge, height:(GridSize size* (ButtonMargin + buttonMargin * 2ButtonSize)) + startButton.HeightFormEdge);
}StartButton.Size = new Size(86, 23);
StartButton.Font = new Font("Arial", 9.75F, FontStyle.Regular);
form.SuspendLayout();
for (int indexStartButton.Text = 0;"New index < blockCountGame"; index++) {
StartButton.UseVisualStyleBackColor = true;
Button button = new Button {
FontStartButton.TabStop = buttonFont,false;
 
Size = new Size(buttonSize, buttonSize),
StartButton.Click //Margin += new PaddingEventHandler(buttonMarginNewGame),;
 
Text = (index + 1).ToString(),
int FormWidth = (GridSize * ButtonSize) + ((GridSize - 1) * ButtonMargin) + (FormEdge * 2);
UseVisualStyleBackColor = true
int FormHeigth = FormWidth }+ StartButton.Height;
 
SetLocation(button, index);
Form Form = new form.Controls.AddForm(button);
buttons[index]Form.Text = button"Fifteen";
Form.ClientSize = new Size(FormWidth, int i = indexFormHeigth);
Form.FormBorderStyle = FormBorderStyle.FixedSingle;
button.Click += (sender, e) => ButtonClick(i);
Form.MaximizeBox = false;
Form.SuspendLayout();
 
for (int i = 0; i < BlockCount; i++)
{
Button Bt = new Button();
Puzzle Pz = new Puzzle(i);
 
int PosX = FormEdge + (Pz.X) * (ButtonSize + ButtonMargin);
int PosY = FormEdge + (Pz.Y) * (ButtonSize + ButtonMargin);
Bt.Location = new Point(PosX, PosY);
 
Bt.Size = new Size(ButtonSize, ButtonSize);
Bt.Font = ButtonFont;
 
Bt.Text = Pz.ToString();
Bt.Tag = Pz;
Bt.UseVisualStyleBackColor = true;
Bt.TabStop = false;
 
Bt.Enabled = false;
if (Pz.IsEmptyPuzzle) Bt.Visible = false;
 
Bt.Click += new EventHandler(MovePuzzle);
 
Puzzles.Add(Bt);
Form.Controls.Add(Bt);
}
 
form.Controls.Add(startButton);
formForm.ResumeLayoutControls.Add(StartButton);
return formForm.ResumeLayout();
 
return Form;
}
 
private void ButtonClickNewGame(intobject Sender, EventArgs iE)
{
do
if (buttons[last].Visible) return;
int target = positionOf[i];{
if for (positionOf[int i] /= gridSize0; ==i positionOf[last]< /Puzzles.Count; gridSizei++) {
while (positionOf[last] < target) {
SwapButton Bt1 = Puzzles[R.Next(lasti, grid[positionOf[lastPuzzles.Count)] + 1]);
moves++Button Bt2 = Puzzles[i];
} Swap(Bt1, Bt2);
while (positionOf[last] > target) {
Swap(last, grid[positionOf[last] - 1]);
moves++;
}
} else if (positionOf[i] % gridSize == positionOf[last] % gridSize) {
while (positionOf[last] < target) {
Swap(last, grid[positionOf[last] + gridSize]);
moves++;
}
while (positionOf[last] > target) {
Swap(last, grid[positionOf[last] - gridSize]);
moves++;
}
}
ifwhile (Solved!IsSolvable()) {;
 
TimeSpan elapsed = DateTime.Now - start;
for (int i = 0; i < Puzzles.Count; i++)
elapsed = TimeSpan.FromSeconds(Math.Round(elapsed.TotalSeconds, 0));
{
buttons[last].Visible = true;
MessageBoxPuzzles[i].Show($"SolvedEnabled in= {moves} moves. Time: {elapsed}")true;
}
}
 
Moves = 0;
bool Solved() => Enumerable.Range(0, blockCount - 1).All(i => positionOf[i] == i);
Start = DateTime.Now;
 
static void SetLocation(Button button, int index)
{
int row = index / gridSize, column = index % gridSize;
button.Location = new Point(
(buttonSize + buttonMargin * 2) * column + buttonMargin + formEdge,
(buttonSize + buttonMargin * 2) * row + buttonMargin + formEdge);
}
 
private void MovePuzzle(object Sender, EventArgs E)
void Shuffle()
{
forButton (int iBt1 = 0(Button)Sender; i < blockCount; i++) {
Puzzle int rPz1 = rnd.Next(i, blockCountPuzzle)Bt1.Tag;
 
int g = grid[r];
Button Bt2 = grid[r]Puzzles.Find(Bt => grid[i]((Puzzle)Bt.Tag).IsEmptyPuzzle);
Puzzle grid[i]Pz2 = g(Puzzle)Bt2.Tag;
 
}
forif (int i = 0; i < blockCount; i++Pz1.NearestWith(Pz2)) {
positionOf[grid[i]] = i;{
SetLocationSwap(buttons[grid[i]]Bt1, iBt2);
Moves++;
}
if (!Solvable()) Swap(0, 1); //Swap any 2 blocks
 
buttons[last].Visible = falseCheckWin();
moves = 0;
start = DateTime.Now;
}
 
boolprivate Solvablevoid CheckWin()
{
Button WrongPuzzle = Puzzles.Find(Bt => !((Puzzle)Bt.Tag).IsTruePlace);
bool parity = true;
forbool (int iUWin = 0;(WrongPuzzle i== < blockCount - 2null); i++) {
 
for (int j = i + 1; j < blockCount - 1; j++) {
if (UWin)
if (positionOf[j] < positionOf[i]) parity = !parity;
{
for (int i = 0; i < Puzzles.Count; i++)
{
Puzzles[i].Enabled = false;
}
 
TimeSpan Elapsed = DateTime.Now - Start;
Elapsed = TimeSpan.FromSeconds(Math.Round(Elapsed.TotalSeconds, 0));
MessageBox.Show(String.Format("Solved in {0} moves. Time: {1}", Moves, Elapsed));
}
if (evenSized && positionOf[last] / gridSize % 2 == 0) parity = !parity;
return parity;
}
 
private void Swap(intButton aBt1, intButton bBt2)
{
Pointif location(Bt1 == Bt2) buttons[a].Locationreturn;
buttons[a].Location = buttons[b].Location;
buttons[b].Location = location;
 
intPuzzle pPz1 = positionOf[a](Puzzle)Bt1.Tag;
positionOf[a]Puzzle Pz2 = positionOf[b](Puzzle)Bt2.Tag;
positionOf[b] = p;
 
grid[positionOf[a]]int g = aPz1.CurrentNumber;
grid[positionOf[b]]Pz1.CurrentNumber = bPz2.CurrentNumber;
Pz2.CurrentNumber = g;
 
Bt1.Visible = true;
Bt1.Text = Pz1.ToString();
if (Pz1.IsEmptyPuzzle) Bt1.Visible = false;
 
Bt2.Visible = true;
Bt2.Text = Pz2.ToString();
if (Pz2.IsEmptyPuzzle) Bt2.Visible = false;
}
 
private bool IsSolvable()
{
// WARNING: size of puzzle board MUST be even(like 4)!
// For explain see: https://www.geeksforgeeks.org/check-instance-15-puzzle-solvable/
 
int InvCount = 0;
for (int i = 0; i < Puzzles.Count - 1; i++)
{
for (int j = i + 1; j < Puzzles.Count; j++)
{
Puzzle Pz1 = (Puzzle)Puzzles[i].Tag;
if (Pz1.IsEmptyPuzzle) continue;
 
Puzzle Pz2 = (Puzzle)Puzzles[j].Tag;
if (Pz2.IsEmptyPuzzle) continue;
 
if (Pz1.CurrentNumber > Pz2.CurrentNumber) InvCount++;
}
}
 
Button EmptyBt = Puzzles.Find(Bt => ((Puzzle)Bt.Tag).IsEmptyPuzzle);
Puzzle EmptyPz = (Puzzle)EmptyBt.Tag;
 
bool Result = false;
if ((EmptyPz.InvY + 1) % 2 == 0) // is even
{
// is odd
if (InvCount % 2 != 0) Result = true;
}
else // is odd
{
// is even
if (InvCount % 2 == 0) Result = true;
}
return Result;
}
}</langsyntaxhighlight>
 
=={{header|C++}}==
<langsyntaxhighlight lang="cpp">
#include <time.h>
#include <stdlib.h>
Line 3,684 ⟶ 4,426:
p15 p; p.play(); return 0;
}
</syntaxhighlight>
</lang>
<pre>
+----+----+----+----+
Line 3,702 ⟶ 4,444:
Tested with GnuCOBOL
 
<langsyntaxhighlight lang="cobol"> >>SOURCE FORMAT FREE
*> This code is dedicated to the public domain
*> This is GNUCOBOL 2.0
Line 3,852 ⟶ 4,594:
end-evaluate
.
end program fifteen.</langsyntaxhighlight>
 
{{out}}
Line 3,871 ⟶ 4,613:
Credit to this post for help with the inversions-counting function: [http://www.lispforum.com/viewtopic.php?f=2&t=3422]
 
Run it (after loading the file) with <syntaxhighlight lang ="lisp">|15|::main</langsyntaxhighlight>.
 
<langsyntaxhighlight lang="lisp">(defpackage :15
(:use :common-lisp))
(in-package :15)
Line 3,971 ⟶ 4,713:
(rotatef (aref *board* (first pos) (second pos))
(aref *board* (first zpos) (second zpos))))))
(format t "You win!~%"))</langsyntaxhighlight>
 
=={{header|Craft Basic}}==
[[File:15 puzzle craft basic.png|thumb]]
<syntaxhighlight lang="basic">rem 15 Puzzle example game
rem written in Craft Basic
rem by Gemino Smothers 2023
rem www.lucidapogee.com
 
define size = 16, correct = 0, moves = 0
define click = 0, start = 0
 
dim list[size]
 
gosub setup
gosub game
 
end
 
sub setup
 
title "15 Puzzle"
 
bgcolor 0,128,0
cls graphics
 
resize 0, 0, 170, 270
center
 
let x = 0
let y = 30
 
for i = 0 to size - 1
 
if x = 112 then
 
let x = 0
let y = y + 25
 
endif
 
let x = x + 28
 
formid i + 1
formtext ""
buttonform x, y, 25, 20
 
next i
 
formid 17
formtext "
staticform 40, 130, 100, 20
bgcolor 0, 128, 0
fgcolor 255, 255, 0
colorform
 
formid 18
formtext ""
staticform 40, 150, 100, 20
bgcolor 0, 128, 0
fgcolor 255, 255, 0
colorform
 
formid 19
formtext "New"
buttonform 1, 1, 50, 20
 
formid 20
formtext "Help"
buttonform 55, 1, 50, 20
 
formid 21
formtext "About"
buttonform 110, 1, 50, 20
 
formid 22
formtext "Welcome."
staticform 40, 170, 120, 20
bgcolor 0, 128, 0
fgcolor 255, 255, 0
colorform
 
return
 
sub shuffle
 
let start = 1
 
formid 22
formtext "shuffling..."
updateform
 
for i = 0 to size - 1
 
formid i + 1
formtext ""
updateform
 
let list[i] = 0
 
next i
 
let t = 0
let i = 0
 
do
 
if i = 14 then
 
let n = 120 - t
 
formid i + 1
formtext n
updateform
 
let list[i] = n
 
break
 
endif
 
for f = 0 to size - 1
 
let n = int(rnd * 15) + 1
let s = 0
 
for c = 0 to i - 1
 
if n = list[c] then
 
let s = 1
break c
 
endif
 
next c
 
if s = 0 and list[i] = 0 then
 
formid i + 1
formtext n
updateform
 
let list[i] = n
let t = t + n
let i = i + 1
 
endif
 
wait
 
next f
 
loop i < size - 1
 
formid 22
formtext ""
updateform
 
return
 
sub game
 
do
 
let click = forms
 
if click > 0 and click < 17 and start = 1 then
 
let moves = moves + 1
 
formid 17
formtext "Moves: ", moves
updateform
 
gosub checkspaces
gosub checkorder
 
endif
 
if click = 19 then
 
gosub shuffle
 
let moves = 0
let correct = 0
 
formid 17
formtext "Moves:"
updateform
 
formid 18
formtext "Correct:"
updateform
 
endif
 
if click = 20 then
 
alert "Click the numbers to move them in the correct order."
 
endif
 
if click = 21 then
 
alert "15 Puzzle", newline, "by Gemino Smothers 2023 ", newline, " www.lucidapogee.com"
 
endif
 
button k, 27
 
wait
 
loop k = 0
 
return
 
sub checkspaces
 
let click = click - 1
let top = click - 4
let right = click + 1
let bottom = click + 4
let left = click - 1
 
if top >= 0 then
 
if list[top] = 0 then
 
let n = top
gosub swap
 
endif
 
endif
 
if right <= size - 1 then
 
if list[right] = 0 then
 
let n = right
gosub swap
 
endif
 
endif
 
if bottom <= size - 1 then
 
if list[bottom] = 0 then
 
let n = bottom
gosub swap
 
endif
 
endif
 
if left >= 0 then
 
if list[left] = 0 then
 
let n = left
gosub swap
 
endif
 
endif
 
return
 
sub swap
 
let t = list[click]
let list[n] = list[click]
let list[click] = 0
 
let click = click + 1
formid click
formtext ""
updateform
 
let n = n + 1
formid n
formtext t
updateform
 
return
 
sub checkorder
 
let correct = 0
 
for i = 0 to size - 2
 
if list[i] = i + 1 then
 
let correct = correct + 1
 
endif
 
next i
 
formid 18
formtext "Correct: ", correct
updateform
 
if correct = size - 1 then
 
wait
alert "You win! Moves: ", moves
 
endif
 
return</syntaxhighlight>
 
=={{header|Delphi}}==
{{works with|Delphi|6.0}}
{{libheader|Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Grids, MMSystem}}
 
This is a pure Delphi version of the program. Rather than use a console based
display, this version uses a Delphi Form for the game and a string grid
for the display. The users selects a move by clicking on the a particular
cell in the grid.
 
<syntaxhighlight lang="Delphi">
 
const BoardWidth = 4; BoardHeight = 4;
const CellCount = BoardWidth * BoardHeight;
 
var GameBoard: array [0..BoardWidth-1,0..BoardHeight-1] of integer;
 
 
procedure BuildBoard;
{Put all number in the game board}
var I,X,Y: integer;
begin
for I:=0 to CellCount-1 do
begin
Y:=I div BoardHeight;
X:=I mod BoardWidth;
GameBoard[X,Y]:=I;
end;
end;
 
 
function IsWinner: boolean;
{Check to see if tiles are winning in order}
var I,X,Y: integer;
begin
Result:=False;
for I:=1 to CellCount-1 do
begin
Y:=(I-1) div BoardHeight;
X:=(I-1) mod BoardWidth;
if GameBoard[X,Y]<>I then exit;
end;
Result:=True;
end;
 
 
procedure DisplayGameBoard(Grid: TStringGrid);
{Display game on TStringGrid component}
var Tile,X,Y: integer;
var S: string;
begin
for Y:=0 to High(GameBoard) do
begin
S:='';
for X:=0 to High(GameBoard[0]) do
begin
Tile:=GameBoard[X,Y];
if Tile=0 then Form1.GameGrid.Cells[X,Y]:=''
else Grid.Cells[X,Y]:=IntToStr(GameBoard[X,Y]);
end;
end;
end;
 
 
 
procedure ExchangePieces(P1,P2: TPoint);
{Exchange the pieces specified by P1 and P2}
var T: integer;
begin
T:=GameBoard[P1.X,P1.Y];
GameBoard[P1.X,P1.Y]:=GameBoard[P2.X,P2.Y];
GameBoard[P2.X,P2.Y]:=T;
end;
 
 
procedure Randomize;
{Scramble piece by exchanging random pieces}
var I: integer;
var P1,P2: TPoint;
begin
for I:=0 to 100 do
begin
P1:=Point(Random(BoardWidth),Random(BoardHeight));
P2:=Point(Random(BoardWidth),Random(BoardHeight));
ExchangePieces(P1,P2);
end;
end;
 
 
procedure NewGame;
{Initiate new game by randomizing tiles}
begin
BuildBoard;
Randomize;
DisplayGameBoard(Form1.GameGrid);
end;
 
 
 
function FindEmptyNeighbor(P: TPoint): TPoint;
{Find the empty neighbor cell if any}
{Returns Point(-1,-1) if none found}
begin
Result:=Point(-1,-1);
if (P.X>0) and (GameBoard[P.X-1,P.Y]=0) then Result:=Point(P.X-1,P.Y)
else if (P.X<(BoardWidth-1)) and (GameBoard[P.X+1,P.Y]=0) then Result:=Point(P.X+1,P.Y)
else if (P.Y>0) and (GameBoard[P.X,P.Y-1]=0) then Result:=Point(P.X,P.Y-1)
else if (P.Y<(BoardHeight-1)) and (GameBoard[P.X,P.Y+1]=0) then Result:=Point(P.X,P.Y+1);
end;
 
 
 
procedure ShowStatus(S: string; BellCount: integer);
{Display status string and ring bell specified number of times}
var I: integer;
begin
Form1.StatusMemo.Lines.Add(S);
for I:=1 to BellCount do PlaySound('DeviceFail', 0, SND_SYNC);
end;
 
 
 
procedure HandleMouseClick(X,Y: integer; Grid: TStringGrid);
{Handle mouse click on specified grid}
var Pos,Empty: TPoint;
var Item: integer;
begin
Grid.MouseToCell(X, Y,Pos.X, Pos.Y);
Item:=GameBoard[Pos.X,Pos.Y];
Empty:=FindEmptyNeighbor(Pos);
if (Item>0) and (Empty.X>=0) then
begin
ExchangePieces(Empty,Pos);
DisplayGameBoard(Grid);
if IsWinner then ShowStatus('Winner', 5);
end
else ShowStatus('Invalid Command.', 1);
end;
 
 
 
procedure TForm1.NewGameBtnClick(Sender: TObject);
{Create new game when button pressed}
begin
NewGame;
end;
 
 
procedure TForm1.GameGridMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
{Use the mouse click event to select a move}
begin
HandleMouseClick(X,Y,GameGrid);
end;
 
procedure TForm1.FormCreate(Sender: TObject);
{Start new game when the program starts running}
begin
NewGame;
end;
</syntaxhighlight>
{{out}}
[[File:Delphi15Puzzle.png|frame|none]]
 
=={{header|EasyLang}}==
 
[https://easylang.onlinedev/apps/15-puzzle.html?v1 Run it]
 
<syntaxhighlight>
<lang>background 432
sys topleft
background 432
textsize 13
len f[] 16
funcproc draw . .
clear
for i range= 1 to 16
h = f[i]
if h < 16
x = (i - 1) mod 4 * 24 + 3
y = (i - 1) div 4 * 24 + 3
color 210
move x y
rect 22 22
move x + 4 y + 56
if h < 10
move x + 6 y + 56
.
color 885
text h
.
.
color 885
text h
.
.
.
global done .
funcproc initsmiley . .
done s = 03.5
for ix range= 1686
f[i]y = i + 186
move x y
.
# shufflecolor 983
for icircle =2.8 14* downto 1s
color 000
r = random (i + 1)
move swapx f[r]- f[i]s y - s
circle s / 3
.
move x + 3.5 y - 3.5
# make it solvable
inv =circle 0s / 3
linewidth s / 3
for i range 15
curve [ x - s y + s x y + 2 * s x + s y + s ]
for j range i
.
if f[j] > f[i]
proc init . .
inv += 1
done = 0
for i = 1 to 16
f[i] = i
.
# shuffle
for i = 15 downto 2
r = randint i
swap f[r] f[i]
.
# make it solvable
inv = 0
for i = 1 to 15
for j = 1 to i - 1
if f[j] > f[i]
inv += 1
.
.
.
if inv mod 2 <> 0
.
if inv mod 2 <>swap 0f[1] f[2]
.
swap f[0] f[1]
textsize 12
.
draw
textsize 12
call draw
.
funcproc move_tile . .
c = mouse_x div 25
r = mouse_y div 25
i = r * 4 + c + 1
if c > 0 and f[i - 1] = 16
swap f[i] f[i - 1]
elif r > 0 and f[i - 4] = 16
swap f[i] f[i - 4]
elif r < 3 and f[i + 4] = 16
swap f[i] f[i + 4]
elif c < 3 and f[i + 1] = 16
swap f[i] f[i + 1]
.
call draw
done for i = 1 to 15
if f[i] > f[i + 1]
for i range 15
if f[i] > f[i + 1]return
done = 0.
.
done = 1
.
if donetimer = 10.5
clear
move 10 30
text "Well done!"
.
.
on mouse_down
if done = 10
call init move_tile
elif done = 3
else
call move_tile init
.
.
on timer
if done = 1
smiley
done = 2
timer 2
else
done = 3
.
.
call init</lang>
</syntaxhighlight>
 
=={{header|F_Sharp|F#}}==
<langsyntaxhighlight lang="fsharp">
// 15 Puzzle Game. Nigel Galloway: August 9th., 2020
let Nr,Nc,RA,rnd=[|3;0;0;0;0;1;1;1;1;2;2;2;2;3;3;3|],[|3;0;1;2;3;0;1;2;3;0;1;2;3;0;1;2|],[|for n in [1..16]->n%16|],System.Random()
Line 4,077 ⟶ 5,319:
[<EntryPoint>]
let main n = fL(match n with [|n|]->let g=[|let g=uint64 n in for n in 60..-4..0->int((g>>>n)&&&15UL)|] in (Array.findIndex((=)0)g,g) |_->fE()) 0
</syntaxhighlight>
</lang>
3 uses:
15 game with no parameters will generate a random game which may be solved.
Line 4,111 ⟶ 5,353:
</pre>
=={{header|Factor}}==
<langsyntaxhighlight lang="factor">USING: accessors combinators combinators.extras
combinators.short-circuit grouping io kernel literals math
math.matrices math.order math.parser math.vectors prettyprint qw
Line 4,187 ⟶ 5,429:
drop ;
 
MAIN: main</langsyntaxhighlight>
{{out}}
<pre>
Line 4,222 ⟶ 5,464:
See [[15_puzzle_solver#Forth]] for a solver based on the same code.
 
<langsyntaxhighlight lang="forth">#! /usr/bin/gforth
 
cell 8 <> [if] s" 64-bit system required" exception throw [then]
Line 4,291 ⟶ 5,533:
 
solution 1000 shuffle play
</syntaxhighlight>
</lang>
 
=={{header|Fortran}}==
Line 4,304 ⟶ 5,546:
The game plan is to start with an ordered array so that each cell definitely has a unique code, then jumble them via "random" swaps. Possible arrangements turn out to have either odd or even parity based on the number of out-of-sequence squares, and as the allowed transformations do not change the parity and the solution state has even parity, odd parity starting states should not be presented except by those following Franz Kafka. The calculation is simplified by always having the blank square in the last position, thus in the last row. Once an even-parity starting state is floundered upon, the blank square is re-positioned using allowable moves so that the parity is not altered thereby. Then the game begins: single-square moves only are considered, though in practice groups of squares could be moved horizontally or vertically rather than one-step-at-a-time - a possible extension.
 
The source style uses F90 for its array arithmetic abilities, especially the functions ALL, ANY and COUNT. A statement <langsyntaxhighlight Fortranlang="fortran">LOCZ = MINLOC(BOARD) !Find the zero. 0 = BOARD(LOCZ(1),LOCZ(2)) == BOARD(ZC,ZR)</langsyntaxhighlight> could be used but is unnecessary thanks to tricks with EQUIVALENCE. For earlier Fortran, various explicit DO-loops would have to be used. This would at least make clear whether or not the equivalents of ANY and ALL terminated on the first failure or doggedly scanned the entire array no matter what. <langsyntaxhighlight Fortranlang="fortran"> SUBROUTINE SWAP(I,J) !Alas, furrytran does not provide this.
INTEGER I,J,T !So, we're stuck with supplying the obvious.
T = I !And, only for one type at a go.
Line 4,420 ⟶ 5,662:
IF (ANY(BORED(1:N - 2) + 1 .NE. BORED(2:N - 1))) GO TO 20 !Are we there yet?
WRITE (MSG,*) TRY,"Steps to success!" !Yes!
END !That was fun.</langsyntaxhighlight>
 
Output: Not so good. As ever, the character cell sizes are not square so a square game board comes out as a rectangle. Similarly, underlining is unavailable (no overprints!) so the layout is not pleasing. There are special "box-drawing" glyphs available, but they are not standardised and there is still no overprinting so that a flabby waste of space results. Further, there is no ability to re-write the display, even though one could easily regard the output to the screen as a random-access file: <code>WRITE (MSG,REC = 6) STUFF</code> would rewrite the sixth line of the display. Instead, output relentlessly rolls forwards, starting as follows:
Line 4,454 ⟶ 5,696:
 
=={{header|FreeBASIC}}==
<langsyntaxhighlight lang="freebasic">sub drawboard( B() as ubyte )
dim as string outstr = ""
for i as ubyte = 0 to 15
Line 4,527 ⟶ 5,769:
print "Congratulations! You win!"
print
drawboard B()</langsyntaxhighlight>
 
 
 
=={{header|FutureBasic}}==
<syntaxhighlight lang="FutureBasic">
// 15 Puzzle // 26 september 2023 //
 
begin globals
CFMutableStringRef board : board = fn MutableStringNew
end globals
 
 
void local fn buildUI
Long i, j, k = 1 // k is button number
window 1, @"15 Puzzle", ( 0, 0, 200, 200 ), 3
for j = 3 to 0 step -1 : for i = 0 to 3 // Top to bottom, left to right
button k, Yes, 1, @"", ( 20 + 40 * i, 20 + 40 * j , 40, 40 ), , NSBezelStyleShadowlessSquare
ControlSetFont(k, fn FontSystemFontOfSize( 21 ) )
ControlSetAlignment( k, NSTextAlignmentCenter )
k ++
next : next
menu 1, , 1, @"File": menu 1, 1, , @"Close", @"w" : MenuItemSetAction( 1, 1, @"performClose:" )
editmenu 2 : menu 2, 0, No : menu 3, , , @"Level"
for i = 1 to 8 : menu 3, i, , str( i ) : next
MenuSetOneItemOnState( 3, 3 )
end fn
 
 
void local fn newGame
CFStringRef s
Long i, m, n = 16, p = 0 // n is empty starting tile, p holds previous move
Bool ok
MutableStringSetString (board, @" 123456789ABCDEF " )
for i = 1 to fn MenuSelectedItem( 3 )^2 // Number of shuffles is square of level
do : ok = Yes
m = n + int( 2.6 * rnd( 4 ) - 6.5 ) // Choose a random move, but
if m < 1 or m > 16 or m == p then ok = No // not of bounds or previous,
if n mod 4 = 0 and m = n + 1 then ok = No // and don't exchange eg tile 4 and 5
if n mod 4 = 1 and m = n - 1 then ok = No // or 9 and 8
until ok = Yes // Found a move, swap in board string
s = mid( board, m, 1 ) : mid( board, m, 1 ) = @" " : mid( board, n, 1 ) = s
p = n : n = m
next
for i = 1 to 16 // Stamp the buttons, p is unicode of board char, s is button title
p = (Long) fn StringCharacterAtIndex( board, i )
if p > 64 then s = fn StringWithFormat ( @"%d", p - 55 ) else s = mid( board, i, 1 )
button i, Yes, 1, s
if fn StringIsEqual( s, @" ") == Yes then button i, No
next
end fn
 
 
void local fn move ( n as Long )
CFStringRef s
Long i, m, x = -1 // x is empty plot
Bool ok
for i = 1 to 4 // see if clicked button is next to empty plot
m = n + int( 2.6 * i - 6.5 ) // -4. -1, +1, +4
ok = Yes
if m < 1 or m > 16 then ok = No // Not out of bounds etc
if n mod 4 = 0 and m = n + 1 then ok = No
if n mod 4 = 1 and m = n - 1 then ok = No
if ok == Yes
if fn StringIsEqual( mid( board, m, 1 ), @" " ) then x = m
end if
next
if x > -1 // Swap places in board string and button titles
s = mid( board, n, 1 ) : mid( board, n, 1 ) = @" " : mid( board, x, 1 ) = s
button x, Yes, 1 , fn ButtonTitle( n ) : button n, No, 1, @" "
end if
if fn StringIsEqual( board, @" 123456789ABCDEF " )
alert 112, , @"Well done.", @"Another game?", @"Yes;No", Yes
end if
end fn
 
 
void local fn doMenu( mnu as Long, itm as Long )
if mnu == 3 then MenuSetOneItemOnState( 3, itm ) : fn newGame
end fn
 
 
void local fn DoDialog( evt as Long, tag as Long )
select evt
case _btnClick : fn move( tag )
case _alertDidEnd : if tag == NSAlertFirstButtonReturn then fn newGame else end
end select
end fn
 
fn buildUI
fn newGame
 
on dialog fn doDialog
on menu fn doMenu
handleevents
</syntaxhighlight>
 
[[File:FB 15.png]]
 
 
=={{header|Gambas}}==
<langsyntaxhighlight lang="gambas">'Charlie Ogier (C) 15PuzzleGame 24/04/2017 V0.1.0 Licenced under MIT
'Inspiration came from: -
''http://rosettacode.org/wiki/15_Puzzle_Game
Line 4,828 ⟶ 6,168:
 
End
</syntaxhighlight>
</lang>
 
[http://www.cogier.com/gambas/Copuzzle.png Click here for image of game in play]
 
=={{header|Go}}==
<langsyntaxhighlight lang="go">package main
 
import (
Line 4,986 ⟶ 6,326:
return
}
}</langsyntaxhighlight>
 
=={{header|Harbour}}==
<syntaxhighlight lang="harbour">
<lang Harbour>
 
#include "inkey.ch"
Line 5,174 ⟶ 6,514:
SetColor(" W/N, BG+/B")
RETURN fim
</syntaxhighlight>
</lang>
 
=={{header|Haskell}}==
<langsyntaxhighlight lang="haskell">import Data.Array
import System.Random
 
Line 5,249 ⟶ 6,589:
randomIndex <- randomRIO (0, length moves - 1)
let move = moves !! randomIndex
shufflePuzzle (numOfShuffels - 1) (applyMove move puzzle)</langsyntaxhighlight>
Output:
<pre>Please enter the difficulty level: 0, 1 or 2
Line 5,279 ⟶ 6,619:
Implementation:
 
<langsyntaxhighlight Jlang="j">require'general/misc/prompt'
 
genboard=:3 :0
Line 5,343 ⟶ 6,683:
showboard board
echo 'You win.'
)</langsyntaxhighlight>
 
Most of this is user interface code. We initially shuffle the numbers randomly, then check their parity and swap the first and last squares if needed. Then, for each move, we allow the user to pick one of the taxicab neighbors of the empty square.
Line 5,349 ⟶ 6,689:
A full game would be too big to be worth showing here, so for the purpose of giving a glimpse of what this looks like in action we replace the random number generator with a constant:
 
<langsyntaxhighlight Jlang="j"> game''
15 puzzle
h for help, q to quit
Line 5,386 ⟶ 6,726:
│13│14│15│ │
└──┴──┴──┴──┘
You win.</langsyntaxhighlight>
 
=={{header|Java}}==
{{works with|Java|8}}
<langsyntaxhighlight lang="java">package fifteenpuzzle;
 
import java.awt.*;
Line 5,603 ⟶ 6,943:
});
}
}</langsyntaxhighlight>
 
=={{header|Javascript}}==
Play it [http://paulo-jorente.de/webgames/15p/ here]
<langsyntaxhighlight lang="javascript">
var board, zx, zy, clicks, possibles, clickCounter, oldzx = -1, oldzy = -1;
function getPossibles() {
Line 5,726 ⟶ 7,066:
restart();
}
</syntaxhighlight>
</lang>
Html to test
<pre>
Line 5,745 ⟶ 7,085:
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">
using Random
 
Line 5,853 ⟶ 7,193:
 
puzzle15play()
</langsyntaxhighlight>{{output}}<pre>
This puzzle is solvable.
+----+----+----+----+
Line 5,870 ⟶ 7,210:
Possible moves are: ["13", "12", "9"], 0 to exit. Your move? =>
</pre>
 
=={{header|Koka}}==
Imperative Version
{{trans|C}}
<syntaxhighlight lang="koka">
import std/num/random
import std/os/readline
struct board
cells: vector<vector<int>>
hole-pos: (int, int)
size: (int, int)
 
type move
MUp
MDown
MLeft
MRight
 
fun delta(move: move): (int, int)
match move
MUp -> (0, 1)
MDown -> (0, -1)
MLeft -> (1, 0)
MRight -> (-1, 0)
 
inline extern unsafe-assign : forall<a> ( v : vector<a>, i : ssize_t, x : a ) -> total ()
c "kk_vector_unsafe_assign"
 
fun update(game: board, move: move): exn board
val (dx, dy) = move.delta
val (hx, hy) = game.hole-pos
val (nx, ny) = (hx + dx, hy + dy)
val (w, h) = game.size
if nx >= 0 && nx < w && ny >= 0 && ny < h then
game.cells[hx].unsafe-assign(hy.ssize_t, game.cells[nx][ny])
game.cells[nx].unsafe-assign(ny.ssize_t, 0)
game(hole-pos=(nx, ny))
else
game
 
val num_shuffles = 100
 
fun random-move(): random move
match random-int() % 4
0 -> MUp
1 -> MDown
2 -> MLeft
_ -> MRight
 
fun setup(size: (int, int)): <exn,random> board
val (w, h) = size
val cells = vector-init(w, fn(x) vector-init(h, fn(y) x*h + y + 1))
var game := Board(cells, (w - 1, h - 1), size)
for(0,num_shuffles) fn(_)
game := game.update(random-move())
game
 
effect break<a>
final ctl break(a: a) : b
 
fun finished(game: board): exn bool
val (w, h) = game.size
var i := 1
with final ctl break(a:bool)
a
for(0,w - 1) fn(x)
for(0,h - 1) fn(y)
if game.cells[x][y] != i then
break(False)
else
i := i + 1
True
 
fun display(game: board): <console,exn> ()
println("")
val (w, h) = game.size
for(0, h - 1) fn(y)
for(0, w - 1) fn(x)
val c = game.cells[x][y]
if c == 0 then
print(" ")
else
print(" " ++ c.show ++ " ")
println("")
println("")
 
fun get_move(): <div,console,exn,break<string>> move
val c = readline()
match c.trim()
"w" -> MUp
"s" -> MDown
"a" -> MLeft
"d" -> MRight
"q" -> break("Finished")
_ -> get_move()
 
fun main()
var game := setup((4, 4))
with final ctl break(a:string)
a.println
while {!game.finished}
game.display
val move = get_move()
game := game.update(move)
println("You won!")
</syntaxhighlight>
 
=={{header|Kotlin}}==
{{trans|Java}}
<langsyntaxhighlight lang="scala">// version 1.1.3
 
import java.awt.BorderLayout
Line 6,021 ⟶ 7,467:
}
}
}</langsyntaxhighlight>
 
=={{header|Liberty BASIC}}==
{{trans|Commodore BASIC}}
{{works with|Just BASIC}}
<syntaxhighlight lang="lb">
<lang lb>
' 15-PUZZLE GAME
' ********************************
Line 6,160 ⟶ 7,606:
isPuzzleComplete = pc
end function
</syntaxhighlight>
</lang>
 
=={{header|LiveCode}}==
<syntaxhighlight lang="livecode">
<lang liveCode>
#Please note that all this code can be performed in livecode with just few mouse clicks
#This is just a pure script exampe
Line 6,225 ⟶ 7,671:
end if
end checkDistance
</syntaxhighlight>
</lang>
Screenshot:
[https://s24.postimg.org/uc6fx7kph/Livecode15_Puzzle_Game.png]
 
=={{header|Lua}}==
<langsyntaxhighlight lang="lua">
math.randomseed( os.time() )
local puz = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }
Line 6,327 ⟶ 7,773:
-- [ entry point ] --
beginGame()
</syntaxhighlight>
</lang>
{{out}}
<pre>
Line 6,359 ⟶ 7,805:
Also the code is not the best, because we can move from 4 position to 5 (we can't do that with real puzzle)
 
<syntaxhighlight lang="m2000 interpreter">
<lang M2000 Interpreter>
Module Puzzle15 {
Line 6,472 ⟶ 7,918:
}
Puzzle15
</syntaxhighlight>
</lang>
 
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<langsyntaxhighlight lang="mathematica">grid = MapThread[{#1,#2} &, {Range @ 16, Range @ 16}]
 
Move[x_] := (empty = Select[grid, #[[1]]==16 &][[1,2]];
Line 6,487 ⟶ 7,933:
CButton[{x_,loc_}] := If[x==16, Null, Button[x,Move @ loc]]
 
Dynamic @ Grid @ Partition[CButton /@ grid,4]</langsyntaxhighlight>
 
=={{header|Mercury}}==
Line 6,493 ⟶ 7,939:
The ideal in Mercury is to have a declarative module that encodes the game logic, and then separate modules to implement a human player with text commands (here), or keyed commands, or some kind of AI player, and so on. fifteen.print/3 is a arguably a smudge on fifteen's interface:
<langsyntaxhighlight Mercurylang="mercury">:- module fifteen.
:- interface.
:- use_module random, io.
Line 6,594 ⟶ 8,040:
move(I, right, !B) :-
not (I = 3 ; I = 7 ; I = 11 ; I = 15),
swap(I, I + 1, !B).</langsyntaxhighlight>
 
As used:
 
<langsyntaxhighlight Mercurylang="mercury">:- module play_fifteen.
:- interface.
:- import_module io.
Line 6,678 ⟶ 8,124:
"
Seed = time(NULL);
").</langsyntaxhighlight>
 
=={{header|MiniScript}}==
===Text-based version===
Text-based game that works with both command-line and [http://miniscript.org/MiniMicro Mini Micro] versions of MiniScript. The Mini Micro has animation showing the changes in the board when the tiles are being shuffled or a move is made.
<syntaxhighlight lang="miniscript">
isMiniMicro = version.hostName == "Mini Micro"
 
// These coordinates are [row,col] not [x,y]
Directions = {"up": [-1,0], "right": [0,1], "down": [1, 0], "left": [0,-1]}
TileNum = range(1, 15)
Puzzle15 = {"grid":[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],
"blankPos": [3,3]}
 
Puzzle15.__setTile = function(position, value)
row = position[0]; col = position[1]
self.grid[row][col] = value
end function
 
Puzzle15.__getTile = function(position)
row = position[0]; col = position[1]
return self.grid[row][col]
end function
 
Puzzle15.__getOppositeDirection = function(direction)
directions = Directions.indexes
oppix = (directions.indexOf(direction) + 2) % 4
return directions[oppix]
end function
 
Puzzle15.getState = function
return self.grid
end function
 
Puzzle15.getBlankPos = function
return self.blankPos
end function
 
Puzzle15.hasWon = function
count = 1
for r in range(0, 3)
for c in range(0, 3)
if self.grid[r][c] != count then return false
count += 1
end for
end for
return true
end function
 
Puzzle15.move = function(direction)
if not Directions.hasIndex(direction) then return false
move = Directions[direction]
curPos = self.blankPos[:]
newPos = [curPos[0] + move[0], curPos[1] + move[1]]
if (-1 < newPos[0] < 4) and (-1 < newPos[1] < 4) then
value = self.__getTile(newPos)
self.__setTile(curPos, value)
self.__setTile(newPos, 16) // 16 is the blank tile
self.blankPos = newPos
return true
else
return false
end if
end function
 
Puzzle15.shuffle = function(n)
lastMove = ""
directions = Directions.indexes
for i in range(1, 50)
while true
moveTo = directions[floor(rnd * 4)]
oppMove = self.__getOppositeDirection(moveTo)
if self.__getOppositeDirection(moveTo) != lastMove and self.move(moveTo) then
lastMove = moveTo
if isMiniMicro then
self.displayBoard
wait 1/33
end if
break
end if
end while
end for
end function
 
Puzzle15.displayBoard = function
if isMiniMicro then clear
for r in range(0, 3)
for c in range(0, 3)
grid = self.getState
if grid[r][c] == 16 then
s = " "
else
s = (" " + grid[r][c])[-3:]
end if
print s, ""
end for
print
end for
end function
 
Puzzle15.shuffle
 
while not Puzzle15.hasWon
if isMiniMicro then
clear
else
print
end if
Puzzle15.displayBoard
while true
print
move = input("Enter the direction to move the blank in (up, down, left, right): ")
move = move.lower
if Directions.hasIndex(move) and Puzzle15.move(move) then break
print "Please enter a valid move."
end while
end while
print "Congratulations! You solved the puzzle!"</syntaxhighlight>
 
===Fully animated version===
This fully animated implementation is for use with [http://miniscript.org/MiniMicro Mini Micro]. It uses assets that come with the Mini Micro as well as a sound file of wood blocks scrapping and hitting each other that is separate from this code. This scrip will still run without it and will not simulate the sound of the tiles moving. You may find this file on [https://github.com/chinhouse/MiniScript/blob/main/Puzzle-15/small_wooden_bricks_movement.mp3 Github].
<syntaxhighlight lang="miniscript">
woodSound = file.loadSound("small_wooden_bricks_movement.mp3")
puzzleTiles = function
gfx.scale = 2
woodImg = file.loadImage("/sys/pics/textures/Wood.png")
gfx.drawImage woodImg,0,0
gfx.color = color.black
gfx.drawRect 6,6, 244,244
bgBoard = new Sprite
bgBoard.image = gfx.getImage(0, 0, 256, 256)
bgBoard.scale = 1.6
bgBoard.x = (960 - 1.6 * 256) / 2 + 128 * 1.6
bgBoard.y = (640 - 1.6 * 256) / 2 + 128 * 1.6
bgBoard.tint = color.rgb(102,51,0)
sprites = [bgBoard]
gfx.clear
gfx.drawImage woodImg,0,0
positions = range(1,15)
positions.shuffle
spriteScale = 1.5
spriteSize = 64
tileXOffset = (960 - 3 * spriteSize * spriteScale) / 2
tileYOffset = (640 - 3 * spriteSize * spriteScale) / 2
for i in range(0,14)
s = str(i+1)
pos = positions[i]
x = pos % 4
y = floor(pos / 4)
xp = x * spriteSize + (spriteSize - (s.len * 14 + 2)) / 2
yp = y * spriteSize + 20
gfx.print s, xp, yp, color.black, "normal"
sprite = new Sprite
sprite.image = gfx.getImage(x * spriteSize, y * spriteSize, spriteSize, spriteSize)
sprite.scale = spriteScale
xs = i % 4; ys = 3 - floor(i / 4)
sprite.x = spriteSize * (xs) * spriteScale + tileXOffset
sprite.y = spriteSize * ys * spriteScale + tileYOffset
sprite.localBounds = new Bounds
sprite.localBounds.width = spriteSize
sprite.localBounds.height = spriteSize
// tint the blocks with different shades of brown
sat = floor(rnd * 47 - 10) * 3
sprite.tint = color.rgb(153 + sat, 102 + sat, 51 + sat)
sprites.push(sprite)
end for
return sprites
end function
 
moveTiles = function(sprites, instructions, t = 6, mute = false)
direction = instructions[0]
delta = Directions[direction]
if not mute and woodSound and direction then woodSound.play 0.1
for i in range(96, 1, -t)
for tile in instructions[1:]
sprites[tile].x += delta[1] * t
sprites[tile].y += -delta[0] * t
end for
wait 1/3200
end for
end function
// These coordinates are [row,col] not [x,y]
Directions = {"up": [-1,0], "right": [0,1], "down": [1, 0], "left": [0,-1]}
TileNum = range(1, 15)
Puzzle15 = {"grid":[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]],
"blankPos": [3,3], "movesToShuffled": [], "movesMade": [], "moveCount": 0}
 
Puzzle15.__setTile = function(position, value)
row = position[0]; col = position[1]
self.grid[row][col] = value
end function
 
Puzzle15.__getTile = function(position)
row = position[0]; col = position[1]
return self.grid[row][col]
end function
 
Puzzle15.__getOppositeDirection = function(direction)
directions = Directions.indexes
oppix = (directions.indexOf(direction) + 2) % 4
return directions[oppix]
end function
 
Puzzle15.__getDirectionToTile = function(n)
for row in range(0, 3)
for col in range(0, 3)
if self.grid[row][col] == n then
dr = row - self.getBlankPos[0]
dc = col - self.getBlankPos[1]
return Directions.indexOf([sign(dr), sign(dc)])
end if
end for
end for
return null
end function
 
Puzzle15.getState = function
return self.grid
end function
 
Puzzle15.getBlankPos = function
return self.blankPos
end function
 
Puzzle15.hasWon = function
count = 1
for r in range(0, 3)
for c in range(0, 3)
if self.grid[r][c] != count then return false
count += 1
end for
end for
return true
end function
 
Puzzle15.move = function(direction)
if not Directions.hasIndex(direction) then return false
move = Directions[direction]
curPos = self.blankPos[:]
newPos = [curPos[0] + move[0], curPos[1] + move[1]]
if (-1 < newPos[0] < 4) and (-1 < newPos[1] < 4) then
value = self.__getTile(newPos)
self.__setTile(curPos, value)
self.__setTile(newPos, 16) // 16 is the blank tile
self.blankPos = newPos
if self.movesMade.len > 0 then
lastMove = self.movesMade[-1]
else
lastMove = ""
end if
if lastMove != "" and self.__getOppositeDirection(lastMove) == direction then
self.movesMade.pop
else
self.movesMade.push(direction)
end if
self.moveCount += 1
return value // return tile that was moved
else
return false
end if
end function
 
Puzzle15.moveNumber = function(n)
direction = Puzzle15.__getDirectionToTile(n)
origDir = direction
if direction == null then return 0
tiles = [self.__getOppositeDirection(direction)]
while origDir == direction
tileNum = self.move(origDir)
tiles.insert(1, tileNum)
direction = self.__getDirectionToTile(n)
end while
return tiles
end function
 
Puzzle15.shuffle = function(n, sprites)
lastMove = ""
directions = Directions.indexes
cnt = 0
instructions = []
while self.movesToShuffled.len < n
if self.movesToShuffled.len == 0 then
lastMove = ""
else
lastMove = self.movesToShuffled[-1]
end if
moveTo = directions[floor(rnd * 4)]
cnt += 1
oppMove = self.__getOppositeDirection(moveTo)
tileMoved = self.move(moveTo)
if oppMove != lastMove and tileMoved then
instructions.push([oppMove, tileMoved])
lastMove = moveTo
self.movesToShuffled.push(moveTo)
else if oppMove == lastMove then
self.movesToShuffled.pop
instructions.pop
end if
end while
for i in instructions
moveTiles(sprites, i, 96, true)
end for
end function
 
clear
display(4).sprites = puzzleTiles
gfx.clear
Puzzle15.shuffle(200, display(4).sprites)
 
while not Puzzle15.hasWon
if mouse.button and not wasPressed then
tile = 16
for i in range(1, 15)
sprite = display(4).sprites[i]
//print sprite.localBounds
if sprite.contains(mouse) then tile = i
end for
if tile != 16 then
instructions = Puzzle15.moveNumber(tile)
if instructions then moveTiles(display(4).sprites, instructions)
end if
end if
wasPressed = mouse.button
yield
end while
fanfare = file.loadSound("/sys/sounds/fanfare.wav")
fanfare.play 0.25
while fanfare.isPlaying
end while
key.get
</syntaxhighlight>
 
=={{header|MUMPS}}==
Line 6,727 ⟶ 8,515:
 
=={{header|Nim}}==
<langsyntaxhighlight lang="nim">import random, terminal
 
type
Line 6,869 ⟶ 8,657:
 
draw board
echo "You win!"</langsyntaxhighlight>
{{out}}
<pre>┌──┬──┬──┬──┐
Line 6,883 ⟶ 8,671:
 
=={{header|OCaml}}==
<langsyntaxhighlight lang="ocaml">module Puzzle =
struct
type t = int array
Line 6,921 ⟶ 8,709:
print_string " > ";
Puzzle.move p (read_line () |> int_of_string)
done</langsyntaxhighlight>
 
To move, input the number to slide into the blank. If you accidentally make an impossible move you can undo it by repeating the last input. A nice self-checked puzzle, the same as if you were physically moving the pieces around.
Line 6,971 ⟶ 8,759:
=={{header|Pascal}}==
This is Free Pascal(version >= 3.0.4) text mode implementation. To make a move, the user needs to enter the number of the selected tile.
<syntaxhighlight lang="pascal">
<lang Pascal>
program fifteen;
{$mode objfpc}
Line 7,261 ⟶ 9,049:
Run;
end.
</syntaxhighlight>
</lang>
 
=={{header|Perl}}==
Line 7,270 ⟶ 9,058:
On verbosity shows how the solvability is calculated. The program has some extra feature like font size and color scheme but also the possibility to set the intial board disposition.
This program was originally posted by me at [http://www.perlmonks.org/?node_id=1192660 perlmonks]
<langsyntaxhighlight lang="perl">
 
use strict;
Line 7,521 ⟶ 9,309:
 
 
</syntaxhighlight>
</lang>
 
===console version===
This short console program just poses solvable puzzles: it achieves this shuffling a solved board n times, where n defaults to 1000 but can be passed as first argument of the program in the command line. It was originally posted by me at [http://www.perlmonks.org/?node_id=1192865 perlmonks] but here a little modification was inserted to prevent wrong numbers to make the board messy.
<langsyntaxhighlight lang="perl">
use strict;
use warnings;
Line 7,557 ⟶ 9,345:
[$$e[0]-1,$$e[1]],[$$e[0]+1,$$e[1]],[$$e[0],$$e[1]-1],[$$e[0],$$e[1]+1]
}
</syntaxhighlight>
</lang>
 
=={{header|Phix}}==
=== console only ===
Kept simple. Obviously, increase the 5 random moves for more of a challenge.
<!--<lang Phix>(phixonline)-->
<syntaxhighlight lang="phix">
<span style="color: #008080;">constant</span> <span style="color: #000000;">ESC<span style="color: #0000FF;">=<span style="color: #000000;">27<span style="color: #0000FF;">,</span> <span style="color: #000000;">UP<span style="color: #0000FF;">=<span style="color: #000000;">328<span style="color: #0000FF;">,</span> <span style="color: #000000;">LEFT<span style="color: #0000FF;">=<span style="color: #000000;">331<span style="color: #0000FF;">,</span> <span style="color: #000000;">RIGHT<span style="color: #0000FF;">=<span style="color: #000000;">333<span style="color: #0000FF;">,</span> <span style="color: #000000;">DOWN<span style="color: #0000FF;">=<span style="color: #000000;">336</span>
constant ESC=27, UP=328, LEFT=331, RIGHT=333, DOWN=336
<span style="color: #004080;">sequence</span> <span style="color: #000000;">board</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">tagset<span style="color: #0000FF;">(<span style="color: #000000;">15<span style="color: #0000FF;">)<span style="color: #0000FF;">&<span style="color: #000000;">0<span style="color: #0000FF;">,</span> <span style="color: #000000;">solve</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">board</span>
sequence board = tagset(15)&0, solve = board
<span style="color: #004080;">integer</span> <span style="color: #000000;">pos</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">16</span>
integer pos = 16
<span style="color: #008080;">procedure</span> <span style="color: #000000;">print_board<span style="color: #0000FF;">(<span style="color: #0000FF;">)</span>
procedure print_board()
<span style="color: #008080;">for</span> <span style="color: #000000;">i<span style="color: #0000FF;">=<span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length<span style="color: #0000FF;">(<span style="color: #000000;">board<span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
for i=1 to length(board) do
<span style="color: #7060A8;">puts<span style="color: #0000FF;">(<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #008080;">iff<span style="color: #0000FF;">(<span style="color: #000000;">i<span style="color: #0000FF;">=<span style="color: #000000;">pos<span style="color: #0000FF;">?<span style="color: #008000;">" "<span style="color: #0000FF;">:<span style="color: #7060A8;">sprintf<span style="color: #0000FF;">(<span style="color: #008000;">"%3d"<span style="color: #0000FF;">,<span style="color: #0000FF;">{<span style="color: #000000;">board<span style="color: #0000FF;">[<span style="color: #000000;">i<span style="color: #0000FF;">]<span style="color: #0000FF;">}<span style="color: #0000FF;">)<span style="color: #0000FF;">)<span style="color: #0000FF;">)</span>
puts(1,iff(i=pos?" ":sprintf("%3d",{board[i]})))
<span style="color: #008080;">if</span> <span style="color: #7060A8;">mod<span style="color: #0000FF;">(<span style="color: #000000;">i<span style="color: #0000FF;">,<span style="color: #000000;">4<span style="color: #0000FF;">)<span style="color: #0000FF;">=<span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">puts<span style="color: #0000FF;">(<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #008000;">"\n"<span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if mod(i,4)=0 then puts(1,"\n") end if
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
end for
<span style="color: #7060A8;">puts<span style="color: #0000FF;">(<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #008000;">"\n"<span style="color: #0000FF;">)</span>
puts(1,"\n")
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
end procedure
<span style="color: #008080;">procedure</span> <span style="color: #000000;">move<span style="color: #0000FF;">(<span style="color: #004080;">integer</span> <span style="color: #000000;">d<span style="color: #0000FF;">)</span>
procedure move(integer d)
<span style="color: #004080;">integer</span> <span style="color: #000000;">new_pos</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pos<span style="color: #0000FF;">+<span style="color: #0000FF;">{<span style="color: #0000FF;">+<span style="color: #000000;">4<span style="color: #0000FF;">,<span style="color: #0000FF;">+<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #0000FF;">-<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #0000FF;">-<span style="color: #000000;">4<span style="color: #0000FF;">}<span style="color: #0000FF;">[<span style="color: #000000;">d<span style="color: #0000FF;">]</span>
integer new_pos = pos+{+4,+1,-1,-4}[d]
<span style="color: #008080;">if</span> <span style="color: #000000;">new_pos<span style="color: #0000FF;">>=<span style="color: #000000;">1</span> <span style="color: #008080;">and</span> <span style="color: #000000;">new_pos<span style="color: #0000FF;"><=<span style="color: #000000;">16</span>
if new_pos>=1 and new_pos<=16
<span style="color: #008080;">and</span> <span style="color: #0000FF;">(<span style="color: #7060A8;">mod<span style="color: #0000FF;">(<span style="color: #000000;">pos<span style="color: #0000FF;">,<span style="color: #000000;">4<span style="color: #0000FF;">)<span style="color: #0000FF;">=<span style="color: #7060A8;">mod<span style="color: #0000FF;">(<span style="color: #000000;">new_pos<span style="color: #0000FF;">,<span style="color: #000000;">4<span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- same col, or row:</span>
and (mod(pos,4)=mod(new_pos,4) -- same col, or row:
<span style="color: #008080;">or</span> <span style="color: #7060A8;">floor<span style="color: #0000FF;">(<span style="color: #0000FF;">(<span style="color: #000000;">pos<span style="color: #0000FF;">-<span style="color: #000000;">1<span style="color: #0000FF;">)<span style="color: #0000FF;">/<span style="color: #000000;">4<span style="color: #0000FF;">)<span style="color: #0000FF;">=<span style="color: #7060A8;">floor<span style="color: #0000FF;">(<span style="color: #0000FF;">(<span style="color: #000000;">new_pos<span style="color: #0000FF;">-<span style="color: #000000;">1<span style="color: #0000FF;">)<span style="color: #0000FF;">/<span style="color: #000000;">4<span style="color: #0000FF;">)<span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
or floor((pos-1)/4)=floor((new_pos-1)/4)) then
<span style="color: #0000FF;">{<span style="color: #000000;">board<span style="color: #0000FF;">[<span style="color: #000000;">pos<span style="color: #0000FF;">]<span style="color: #0000FF;">,<span style="color: #000000;">board<span style="color: #0000FF;">[<span style="color: #000000;">new_pos<span style="color: #0000FF;">]<span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{<span style="color: #000000;">board<span style="color: #0000FF;">[<span style="color: #000000;">new_pos<span style="color: #0000FF;">]<span style="color: #0000FF;">,<span style="color: #000000;">0<span style="color: #0000FF;">}</span>
{board[pos],board[new_pos]} = {board[new_pos],0}
<span style="color: #000000;">pos</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">new_pos</span>
pos = new_pos
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
end if
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
end procedure
<span style="color: #008080;">for</span> <span style="color: #000000;">i<span style="color: #0000FF;">=<span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">5</span> <span style="color: #008080;">do</span> <span style="color: #000000;">move<span style="color: #0000FF;">(<span style="color: #7060A8;">rand<span style="color: #0000FF;">(<span style="color: #000000;">4<span style="color: #0000FF;">)<span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
for i=1 to 5 do move(rand(4)) end for
<span style="color: #008080;">while</span> <span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
while 1 do
<span style="color: #000000;">print_board<span style="color: #0000FF;">(<span style="color: #0000FF;">)</span>
print_board()
<span style="color: #008080;">if</span> <span style="color: #000000;">board<span style="color: #0000FF;">=<span style="color: #000000;">solve</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if board=solve then exit end if
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find<span style="color: #0000FF;">(<span style="color: #7060A8;">wait_key<span style="color: #0000FF;">(<span style="color: #0000FF;">)<span style="color: #0000FF;">,<span style="color: #0000FF;">{<span style="color: #000000;">ESC<span style="color: #0000FF;">,<span style="color: #000000;">UP<span style="color: #0000FF;">,<span style="color: #000000;">LEFT<span style="color: #0000FF;">,<span style="color: #000000;">RIGHT<span style="color: #0000FF;">,<span style="color: #000000;">DOWN<span style="color: #0000FF;">}<span style="color: #0000FF;">)<span style="color: #0000FF;">-<span style="color: #000000;">1</span>
integer c = find(wait_key(),{ESC,UP,LEFT,RIGHT,DOWN})-1
<span style="color: #008080;">if</span> <span style="color: #000000;">c<span style="color: #0000FF;">=<span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
if c=0 then exit end if
<span style="color: #000000;">move<span style="color: #0000FF;">(<span style="color: #000000;">c<span style="color: #0000FF;">)</span>
move(c)
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
end while
<span style="color: #7060A8;">puts<span style="color: #0000FF;">(<span style="color: #000000;">1<span style="color: #0000FF;">,<span style="color: #008000;">"solved!\n"<span style="color: #0000FF;">)
puts(1,"solved!\n")
<!--</lang>-->
</syntaxhighlight>
{{out}}
<pre>
Line 7,624 ⟶ 9,413:
solved!
</pre>
 
=== GUI version ===
{{libheader|Phix/pGUI}}
{{libheader|Phix/online}}
You can run this online [http://phix.x10.mx/p2js/15game.htm here].
<!--<langsyntaxhighlight Phixlang="phix">(phixonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\15_puzzle_game.exw
Line 7,753 ⟶ 9,543:
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<!--</langsyntaxhighlight>-->
 
=={{header|PHP}}==
OOP and MVC design pattern has been used to facilitate any improvements, e.g. tiles with graphics instead of just numbers.
<langsyntaxhighlight PHPlang="php"><?php
// Puzzle 15 Game - Rosseta Code - PHP 7 as the server-side script language.
 
Line 7,982 ⟶ 9,772:
?></p>
</body>
</html></langsyntaxhighlight>
 
=={{header|Picat}}==
<syntaxhighlight lang="picat">
<lang Picat>
import util.
 
Line 8,050 ⟶ 9,840:
Board[R0,C0] := S,
Board[R,C] := 0.
</syntaxhighlight>
</lang>
 
=={{header|Powershell}}==
Line 8,057 ⟶ 9,847:
Board is always scrambled from a solved board so it's always solvable.
Written in ISE with ISESteroids. I'm sure the code could be improved on.
<syntaxhighlight lang="powershell">
<lang Powershell>
#15 Puzzle Game
$Script:Neighbours = @{
Line 8,317 ⟶ 10,107:
# Show Window
$result = Show-WPFWindow -Window $window
</syntaxhighlight>
</lang>
 
=={{header|Processing}}==
<langsyntaxhighlight lang="java">
int number_of_grid_cells = 16; // Set the number of cells of the board here 9, 16, 25 etc
color piece_color = color(255, 175, 0);
Line 8,434 ⟶ 10,224:
}
}
}</langsyntaxhighlight>
 
'''It can be played on line''' :<BR> [https://www.openprocessing.org/sketch/863055/ here.]
Line 8,440 ⟶ 10,230:
==={{header|Processing Python mode}}===
{{trans|Processing}}
<langsyntaxhighlight Pythonlang="python"># Set the number of cells of the board here 9, 16, 25 etc
num_grid_cells = 16
piece_color = color(255, 175, 0)
Line 8,550 ⟶ 10,340:
# Upper bright
line(self.x + 2, self.y + 1, self.x +
piece_side_length - 1, self.y + 1)</langsyntaxhighlight>
 
 
=={{header|Prolog}}==
Works with SWI-Prolog
<syntaxhighlight lang="prolog">
% map the sequence of cells
next_cell(1,Y,2,Y).
next_cell(2,Y,3,Y).
next_cell(3,Y,4,Y).
next_cell(4,1,1,2).
next_cell(4,2,1,3).
next_cell(4,3,1,4).
 
% map the game items, and how to print
item(1,2, ' 1 ').
item(2,3, ' 2 ').
item(3,4, ' 3 ').
item(4,5, ' 4 ').
item(5,6, ' 5 ').
item(6,7, ' 6 ').
item(7,8, ' 7 ').
item(8,9, ' 8 ').
item(9,10, ' 9 ').
item(10,11, ' 10 ').
item(11,12, ' 11 ').
item(12,13, ' 12 ').
item(13,14, ' 13 ').
item(14,15, ' 14 ').
item(15,0, ' 15 ').
 
% Move - find the current blank space, and the new place and swap them
move_1(1,2).
move_1(2,3).
move_1(3,4).
 
move(up, X, Y, X, Y1) :- move_1(Y1,Y).
move(down, X, Y, X, Y1) :- move_1(Y, Y1).
move(left, X, Y, X1, Y) :- move_1(X1, X).
move(right, X, Y, X1, Y) :- move_1(X, X1).
 
move(Dir, Board, [p(X1,Y1,0),p(X,Y,P)|B2]) :-
select(p(X, Y, 0), Board, B1),
move(Dir, X,Y,X1,Y1),
select(p(X1, Y1, P), B1, B2).
 
% Solved - the game is solved if running through the next_cell sequence produces the item sequence.
solved(Board) :-
select(p(1,1,1),Board,Remaining),
solved(Remaining,p(1,1,1)).
solved([p(4,4,0)], p(_,_,15)).
solved(Board,p(Xp,Yp,Prev)) :-
item(Prev,P,_),
select(p(X,Y,P),Board,Remaining),
next_cell(Xp,Yp,X,Y),
solved(Remaining, p(X,Y,P)).
 
% Print - run through the next_cell sequence printing the found items.
print(Board) :-
select(p(1,1,P),Board,Remaining),
nl, write_cell(P),
print(Remaining,p(1,1)).
print([],_) :- nl.
print(Board,p(X,Y)) :-
next_cell(X,Y,X1,Y1),
select(p(X1,Y1,P),Board,Remaining),
write_cell(P),
end_line_or_not(X1),
print(Remaining,p(X1,Y1)).
 
write_cell(0) :- write(' ').
write_cell(P) :- item(P,_,W), write(W).
 
end_line_or_not(1).
end_line_or_not(2).
end_line_or_not(3).
end_line_or_not(4) :- nl.
 
% Next Move - get the player input.
next_move_by_player(Move) :-
repeat,
get_single_char(Char),
key_move(Char, Move).
 
key_move(119, up). % 'w'
key_move(115, down). % 'd'
key_move(97, left). % 'a'
key_move(100, right). % 'd'
 
% Play - loop over the player moves until solved.
play(Board) :-
solved(Board) -> (
print(Board),
write('You Win!\n'))
; (
print(Board),
next_move_by_player(Move),
move(Move, Board, NewBoard),
play(NewBoard)).
 
play :-
test_board(Board),
play(Board),
!.
test_board(
[p(1,1,9),p(2,1,1),p(3,1,4),p(4,1,7),
p(1,2,6),p(2,2,5),p(3,2,3),p(4,2,2),
p(1,3,13),p(2,3,10),p(3,3,0),p(4,3,8),
p(1,4,14),p(2,4,15),p(3,4,11),p(4,4,12)]).
</syntaxhighlight>
 
=={{header|PureBasic}}==
Line 8,556 ⟶ 10,456:
Numbers are displayed in Hexadecimal (ie 1 to F)
Default controls are u,d,l,r
<syntaxhighlight lang="purebasic">
<lang PureBasic>
#difficulty=10 ;higher is harder
#up="u"
Line 8,626 ⟶ 10,526:
Print("Won !")
CloseConsole()
</syntaxhighlight>
</lang>
<pre>
1234
Line 8,640 ⟶ 10,540:
{{works with|Python|3.X}}
'''unoptimized'''
<langsyntaxhighlight lang="python">
''' Structural Game for 15 - Puzzle with different difficulty levels'''
from random import randint
Line 8,765 ⟶ 10,665:
print('You WON')
break
</syntaxhighlight>
</lang>
<pre>
Enter the difficulty : 0 1 2
Line 8,799 ⟶ 10,699:
 
===Python: using tkinter===
<langsyntaxhighlight lang="python">
''' Python 3.6.5 code using Tkinter graphical user interface.'''
 
Line 9,049 ⟶ 10,949:
g = Game(root)
root.mainloop()
</syntaxhighlight>
</lang>
 
=={{header|QB64}}==
<syntaxhighlight lang="qb64">
<lang QB64>
_TITLE "GUI Sliding Blocks Game "
RANDOMIZE TIMER
Line 9,136 ⟶ 11,036:
END IF
LOOP
</syntaxhighlight>
</lang>
 
=={{header|Quackery}}==
The inputs are w,s,a,d and they refer to the direction in which the empty place "moves". After every prompt you can input a string of characters and submit them by pressing enter. Any characters other than w,s,a,d will be ignored, and moves will be performed according to the correct commands. Commands that would "move" the empty space off the edge of the puzzle are also ignored. For example, on a solved puzzle "asgw" would move 15 to the right, ignore 's' and 'g' and move 11 down.
<syntaxhighlight lang="quackery">
<lang Quackery>
( moves: 0 - up, 1 - down, 2 - left, 3 - right )
 
Line 9,230 ⟶ 11,130:
 
15-puzzle
</syntaxhighlight>
</lang>
 
=={{header|R}}==
The inputs are w,a,s,d and they refer to the direction in which the adjacent piece moves into the empty space. For example, on a solved puzzle, "d" would move the 15 to the right.
<syntaxhighlight lang="r">
<lang R>
puz15<-function(scramble.length=100){
m=matrix(c(1:15,0),byrow=T,ncol=4)
Line 9,323 ⟶ 11,223:
}
}
</syntaxhighlight>
</lang>
 
Sample output:
Line 9,370 ⟶ 11,270:
It uses the <code>2htdp/universe</code> package.
 
<langsyntaxhighlight lang="racket">#lang racket/base
(require 2htdp/universe 2htdp/image racket/list racket/match)
 
Line 9,418 ⟶ 11,318:
(to-draw (fifteen->pict))
(stop-when solved-world? (fifteen->pict #t))
(on-key key-move-space))</langsyntaxhighlight>
 
=={{header|Raku}}==
Line 9,424 ⟶ 11,324:
{{works with|Rakudo|2018.06}}
Most of this is interface code. Reused substantial portions from the [[2048#Raku|2048]] task. Use the arrow keys to slide tiles, press 'q' to quit or 'n' for a new puzzle. Requires a POSIX termios aware terminal. Ensures that the puzzle is solvable by shuffling the board with an even number of swaps, then checking for even taxicab parity for the empty space.
<syntaxhighlight lang="raku" perl6line>use Term::termios;
 
constant $saved = Term::termios.new(fd => 1).getattr;
Line 9,538 ⟶ 11,438:
last if $key eq 'q'; # (q)uit
new() if $key eq 'n';
}</langsyntaxhighlight>
Sample screen shot:
<pre>
Line 9,557 ⟶ 11,457:
 
=={{header|Rebol}}==
<langsyntaxhighlight Rebollang="rebol">rebol [] random/seed now g: [style t box red [
if not find [0x108 108x0 0x-108 -108x0] face/offset - e/offset [exit]
x: face/offset face/offset: e/offset e/offset: x] across
] x: random repeat i 15 [append x:[] i] repeat i 15 [
repend g ['t mold x/:i random white] if find [4 8 12] i [append g 'return]
] append g [e: box] view layout g</langsyntaxhighlight>
 
=={{header|Red}}==
<syntaxhighlight lang="red">
<lang Red>
Red [Needs: 'view]
system/view/screens/1/pane/-1/visible?: false ;; suppress console window
Line 9,631 ⟶ 11,531:
view lay
 
</syntaxhighlight>
</lang>
 
=={{header|REXX}}==
Line 9,640 ⟶ 11,540:
 
Over half of the REXX program has to do with input validation and presentation of the puzzle (grid).
<langsyntaxhighlight lang="rexx">/*REXX pgm implements the 15─puzzle (AKA: Gem Puzzle, Boss Puzzle, Mystic Square, 14─15)*/
parse arg N seed . /*obtain optional arguments from the CL*/
if N=='' | N=="," then N=4 /*Not specified? Then use the default.*/
Line 9,703 ⟶ 11,603:
rm=holeRow-1; rp=holeRow+1; cm=holeCol-1; cp=holeCol+1 /*possible moves.*/
!=!.rm.holeCol !.rp.holeCol !.holeRow.cm !.holeRow.cp /* legal moves.*/
if show then say pad bot; return</langsyntaxhighlight>
{{out|output|text=&nbsp; when using the default input:}}
<pre>
Line 9,749 ⟶ 11,649:
=={{header|Ring}}==
 
<langsyntaxhighlight lang="ring">
# Project : CalmoSoft Fifteen Puzzle Game
# Date : 2018/12/01
Line 10,516 ⟶ 12,416:
return
 
</syntaxhighlight>
</lang>
 
'''Output:'''
 
[https://youtu.be/IZqpYctv8Zs CalmoSoft Fifteen Puzzle Game in Ring - video]
 
=== newer version ===
This was added, out of place, 3rd November 2023, but dated a year earlier than the one last updated 29th September 2021...
<syntaxhighlight lang="ring">
/*
**
** Game : CalmoSoft Fifteen Puzzle Game 3D
** Date : 2017/09/01
** Author : CalmoSoft <calmosoft@gmail.com>, Mahmoud Fayed
**
*/
 
# Load Libraries
load "gamelib.ring" # RingAllegro Library
load "opengl21lib.ring" # RingOpenGL Library
 
butSize = 3
texture = list(9)
cube = list(9)
rnd = list(9)
rndok = 0
 
for n=1 to 9
rnd[n] = 0
next
 
for n=1 to 9
while true
rndok = 0
ran = random(8) + 1
for nr=1 to 9
if rnd[nr] = ran
rndok = 1
ok
next
if rndok = 0
rnd[n] = ran
exit
ok
end
next
 
for n=1 to 9
if rnd[n] = 9
empty = n
ok
next
 
#==============================================================
# To Support MacOS X
al_run_main()
func al_game_start # Called by al_run_main()
main() # Now we call our main function
#==============================================================
 
func main
new TicTacToe3D {
start()
}
 
class TicTacToe3D from GameLogic
 
FPS = 60
TITLE = "CalmoSoft Fifteen Puzzle Game 3D"
 
oBackground = new GameBackground
oGameSound = new GameSound
oGameCube = new GameCube
oGameInterface = new GameInterface
 
func loadresources
oGameSound.loadresources()
oBackGround.loadresources()
oGameCube.loadresources()
 
func drawScene
oBackground.update()
oGameInterface.update(self)
 
func MouseClickEvent
oGameInterface.MouseClickEvent(self)
 
class GameInterface
 
func Update oGame
prepare()
cubes(oGame)
 
func Prepare
w = 1024 h = 768
ratio = w / h
glViewport(0, 0, w, h)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(-120,ratio,1,120)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
glEnable(GL_TEXTURE_2D)
glShadeModel(GL_SMOOTH)
glClearColor(0.0, 0.0, 0.0, 0.5)
glClearDepth(1.0)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
glDepthFunc(GL_LEQUAL)
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
 
func Cubes oGame
oGame.oGameCube {
aGameMap = oGame.aGameMap
cube[1] = cube( 5 , -3 , -5 , texture[rnd[1]] )
cube[2] = cube( 0 , -3 , -5 , texture[rnd[2]] )
cube[3] = cube( -5 , -3 , -5 , texture[rnd[3]] )
cube[4] = cube( 5 , 1 , -5 , texture[rnd[4]] )
cube[5] = cube( 0 , 1 , -5 , texture[rnd[5]] )
cube[6] = cube( -5 , 1 , -5 , texture[rnd[6]] )
cube[7] = cube( 5 , 5 , -5 , texture[rnd[7]] )
cube[8] = cube( 0 , 5 , -5 , texture[rnd[8]] )
cube[9] = cube( -5 , 5 , -5 , texture[rnd[9]] )
rotate()
}
 
func MouseClickEvent oGame
oGame {
aBtn = Point2Button(Mouse_X,Mouse_Y)
move = 0
nRow = aBtn[1]
nCol = aBtn[2]
tile = (nRow-1)*3 + nCol
up = (empty = (tile - butSize))
down = (empty = (tile + butSize))
left = ((empty = (tile- 1)) and ((tile % butSize) != 1))
right = ((empty = (tile + 1)) and ((tile % butSize) != 0))
move = up or down or left or right
if move = 1
temp = rnd[empty]
rnd[empty] = rnd[tile]
rnd[tile] = temp
empty = tile
oGame.oGameCube {
aGameMap = oGame.aGameMap
cube[1] = cube( 5 , -3 , -5 , texture[rnd[1]] )
cube[2] = cube( 0 , -3 , -5 , texture[rnd[2]] )
cube[3] = cube( -5 , -3 , -5 , texture[rnd[3]] )
cube[4] = cube( 5 , 1 , -5 , texture[rnd[4]] )
cube[5] = cube( 0 , 1 , -5 , texture[rnd[5]] )
cube[6] = cube( -5 , 1 , -5 , texture[rnd[6]] )
cube[7] = cube( 5 , 5 , -5 , texture[rnd[7]] )
cube[8] = cube( 0 , 5 , -5 , texture[rnd[8]] )
cube[9] = cube( -5 , 5 , -5 , texture[rnd[9]] )
rotate()
}
ok
}
 
Class GameLogic from GraphicsAppBase
 
aGameMap = [
[ :n , :n , :n ] ,
[ :n , :n , :n ] ,
[ :n , :n , :n ]
]
 
aGameButtons = [ # x1,y1,x2,y2
[176,88,375,261], # [1,1]
[423,88,591,261], # [1,2]
[645,88,876,261], # [1,3]
[176,282,375,428], # [2,1]
[423,282,591,428], # [2,2]
[645,282,876,428], # [2,3]
[176,454,375,678], # [3,1]
[423,454,591,678], # [3,2]
[645,454,876,678] # [3,3]
]
 
cActivePlayer = :x
 
func point2button x,y
nRow = 0
nCol = 0
for t = 1 to len(aGameButtons)
rect = aGameButtons[t]
if x >= rect[1] and x <= rect[3] and
y >= rect[2] and y <= rect[4]
switch t
on 1 nRow = 1 nCol = 1
on 2 nRow = 1 nCol = 2
on 3 nRow = 1 nCol = 3
on 4 nRow = 2 nCol = 1
on 5 nRow = 2 nCol = 2
on 6 nRow = 2 nCol = 3
on 7 nRow = 3 nCol = 1
on 8 nRow = 3 nCol = 2
on 9 nRow = 3 nCol = 3
off
exit
ok
next
return [nRow,nCol]
 
class GameCube
 
bitmap bitmap2 bitmap3
textureX textureO textureN
 
xrot = 0.0
yrot = 0.0
zrot = 0.0
 
func loadresources
bitmp1 = al_load_bitmap("image/n1.jpg")
texture[1] = al_get_opengl_texture(bitmp1)
bitmp2 = al_load_bitmap("image/n2.jpg")
texture[2] = al_get_opengl_texture(bitmp2)
bitmp3 = al_load_bitmap("image/n3.jpg")
texture[3] = al_get_opengl_texture(bitmp3)
bitmp4 = al_load_bitmap("image/n4.jpg")
texture[4] = al_get_opengl_texture(bitmp4)
bitmp5 = al_load_bitmap("image/n5.jpg")
texture[5] = al_get_opengl_texture(bitmp5)
bitmp6 = al_load_bitmap("image/n6.jpg")
texture[6] = al_get_opengl_texture(bitmp6)
bitmp7 = al_load_bitmap("image/n7.jpg")
texture[7] = al_get_opengl_texture(bitmp7)
bitmp8 = al_load_bitmap("image/n8.jpg")
texture[8] = al_get_opengl_texture(bitmp8)
bitmp9 = al_load_bitmap("image/empty.png")
texture[9] = al_get_opengl_texture(bitmp9)
 
func cube(x,y,z,nTexture)
glLoadIdentity()
glTranslatef(x,y,z)
glRotatef(xrot,1.0,0.0,0.0)
glRotatef(yrot,0.0,1.0,0.0)
glRotatef(zrot,0.0,0.0,1.0)
setCubeTexture(nTexture)
drawCube()
 
func setCubeTexture cTexture
glBindTexture(GL_TEXTURE_2D, cTexture)
 
func Rotate
xrot += 0.3 * 5
yrot += 0.2 * 5
zrot += 0.4 * 5
 
func drawcube
glBegin(GL_QUADS)
// Front Face
glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, 1.0)
glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, 1.0)
glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, 1.0)
glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, 1.0)
// Back Face
glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, 1.0, -1.0)
glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, 1.0, -1.0)
glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, -1.0)
// Top Face
glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, -1.0)
glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, 1.0, 1.0)
glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, 1.0, 1.0)
glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, -1.0)
// Bottom Face
glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, -1.0, -1.0)
glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, 1.0)
glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, 1.0)
// Right face
glTexCoord2f(1.0, 0.0) glVertex3f( 1.0, -1.0, -1.0)
glTexCoord2f(1.0, 1.0) glVertex3f( 1.0, 1.0, -1.0)
glTexCoord2f(0.0, 1.0) glVertex3f( 1.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0) glVertex3f( 1.0, -1.0, 1.0)
// Left Face
glTexCoord2f(0.0, 0.0) glVertex3f(-1.0, -1.0, -1.0)
glTexCoord2f(1.0, 0.0) glVertex3f(-1.0, -1.0, 1.0)
glTexCoord2f(1.0, 1.0) glVertex3f(-1.0, 1.0, 1.0)
glTexCoord2f(0.0, 1.0) glVertex3f(-1.0, 1.0, -1.0)
glEnd()
 
 
class GameBackground
 
nBackX = 0
nBackY = 0
nBackDiffx = -1
nBackDiffy = -1
nBackMotion = 1
aBackMotionList = [
[ -1, -1 ] , # Down - Right
[ 0 , 1 ] , # Up
[ -1, -1 ] , # Down - Right
[ 0 , 1 ] , # Up
[ 1 , -1 ] , # Down - Left
[ 0 , 1 ] , # Up
[ 1 , -1 ] , # Down - Left
[ 0 , 1 ] # Up
]
 
bitmap
 
func Update
draw()
motion()
 
func draw
al_draw_bitmap(bitmap,nBackX,nBackY,1)
 
func motion
nBackX += nBackDiffx
nBackY += nBackDiffy
if (nBackY = -350) or (nBackY = 0)
nBackMotion++
if nBackMotion > len(aBackMotionList)
nBackMotion = 1
ok
nBackDiffx = aBackMotionList[nBackMotion][1]
nBackDiffy = aBackMotionList[nBackMotion][2]
ok
 
func loadResources
bitmap = al_load_bitmap("image/back.jpg")
 
class GameSound
 
sample sampleid
 
func loadresources
sample = al_load_sample( "sound/music1.wav" )
sampleid = al_new_allegro_sample_id()
al_play_sample(sample, 1.0, 0.0,1.0,ALLEGRO_PLAYMODE_LOOP,sampleid)
 
class GraphicsAppBase
 
display event_queue ev timeout
timer
redraw = true
FPS = 60
SCREEN_W = 1024
SCREEN_H = 700
KEY_UP = 1
KEY_DOWN = 2
KEY_LEFT = 3
KEY_RIGHT = 4
Key = [false,false,false,false]
Mouse_X = 0
Mouse_Y = 0
TITLE = "Graphics Application"
PRINT_MOUSE_XY = False
 
func start
SetUp()
loadResources()
eventsLoop()
destroy()
 
func setup
al_init()
al_init_font_addon()
al_init_ttf_addon()
al_init_image_addon()
al_install_audio()
al_init_acodec_addon()
al_reserve_samples(1)
al_set_new_display_flags(ALLEGRO_OPENGL)
display = al_create_display(SCREEN_W,SCREEN_H)
al_set_window_title(display,TITLE)
al_clear_to_color(al_map_rgb(0,0,0))
event_queue = al_create_event_queue()
al_register_event_source(event_queue,
al_get_display_event_source(display))
ev = al_new_allegro_event()
timeout = al_new_allegro_timeout()
al_init_timeout(timeout, 0.06)
timer = al_create_timer(1.0 / FPS)
al_register_event_source(event_queue,
al_get_timer_event_source(timer))
al_start_timer(timer)
al_install_mouse()
al_register_event_source(event_queue,
al_get_mouse_event_source())
al_install_keyboard()
al_register_event_source(event_queue,
al_get_keyboard_event_source())
 
func eventsLoop
while true
al_wait_for_event_until(event_queue, ev, timeout)
switch al_get_allegro_event_type(ev)
on ALLEGRO_EVENT_DISPLAY_CLOSE
CloseEvent()
on ALLEGRO_EVENT_TIMER
redraw = true
on ALLEGRO_EVENT_MOUSE_AXES
mouse_x = al_get_allegro_event_mouse_x(ev)
mouse_y = al_get_allegro_event_mouse_y(ev)
if PRINT_MOUSE_XY
see "x = " + mouse_x + nl
see "y = " + mouse_y + nl
ok
on ALLEGRO_EVENT_MOUSE_ENTER_DISPLAY
mouse_x = al_get_allegro_event_mouse_x(ev)
mouse_y = al_get_allegro_event_mouse_y(ev)
on ALLEGRO_EVENT_MOUSE_BUTTON_UP
MouseClickEvent()
on ALLEGRO_EVENT_KEY_DOWN
switch al_get_allegro_event_keyboard_keycode(ev)
on ALLEGRO_KEY_UP
key[KEY_UP] = true
on ALLEGRO_KEY_DOWN
key[KEY_DOWN] = true
on ALLEGRO_KEY_LEFT
key[KEY_LEFT] = true
on ALLEGRO_KEY_RIGHT
key[KEY_RIGHT] = true
off
on ALLEGRO_EVENT_KEY_UP
switch al_get_allegro_event_keyboard_keycode(ev)
on ALLEGRO_KEY_UP
key[KEY_UP] = false
on ALLEGRO_KEY_DOWN
key[KEY_DOWN] = false
on ALLEGRO_KEY_LEFT
key[KEY_LEFT] = false
on ALLEGRO_KEY_RIGHT
key[KEY_RIGHT] = false
on ALLEGRO_KEY_ESCAPE
exit
off
off
if redraw and al_is_event_queue_empty(event_queue)
redraw = false
drawScene()
al_flip_display()
ok
callgc()
end
 
func destroy
al_destroy_timer(timer)
al_destroy_allegro_event(ev)
al_destroy_allegro_timeout(timeout)
al_destroy_event_queue(event_queue)
al_destroy_display(display)
al_exit()
 
func loadresources
 
func drawScene
 
func MouseClickEvent
exit # Exit from the Events Loop
 
func CloseEvent
exit # Exit from the Events Loop
 
</syntaxhighlight>
[https://youtu.be/xvhNKTZKi8U CalmoSoft Fifteen Puzzle Game in 3D]
 
=={{header|Ruby}}==
<langsyntaxhighlight lang="ruby">require 'io/console'
 
class Board
Line 10,612 ⟶ 12,971:
end
 
Board.new</langsyntaxhighlight>
 
{{out}}
Line 10,632 ⟶ 12,991:
{{libheader|JRubyArt}}
Gui Version:-
<langsyntaxhighlight lang="ruby">DIM = 100
SOLUTION = %w[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0].freeze
 
Line 10,735 ⟶ 13,094:
end
 
</syntaxhighlight>
</lang>
 
=={{header|Run BASIC}}==
<langsyntaxhighlight lang="runbasic">call SetCSS
' ---- fill 15 squares with 1 to 15
dim sq(16)
Line 10,806 ⟶ 13,165:
Text-Align:Center;Font-Size:24pt;Font-Weight:Bold;Font-Family:Arial;
}"
END SUB</langsyntaxhighlight>
Output:
[[File:KokengeGame15.jpg]]
Line 10,812 ⟶ 13,171:
=={{header|Rust}}==
{{libheader|rand}}
<syntaxhighlight lang ="rust">extern crate rand;
extern crate rand;
use clearscreen::clear;
use std::collections::HashMap;
use std::fmt;
use std::io::{self, Write};
 
use rand::seq::SliceRandom;
use rand::Rng;
 
use rand::seq::SliceRandom;
#[derive(Copy, Clone, PartialEq, Debug)]
enum Cell {
Line 10,825 ⟶ 13,186:
Empty,
}
 
#[derive(Eq, PartialEq, Hash, Debug)]
enum Direction {
Line 10,833 ⟶ 13,194:
Right,
}
 
enum Action {
Move(Direction),
Quit,
}
 
type Board = [Cell; 16];
const EMPTY: Board = [Cell::Empty; 16];
 
struct P15 {
board: Board,
}
 
impl P15 {
fn new() -> Self {
Line 10,852 ⟶ 13,213:
*cell = Cell::Card(i);
}
 
let mut rng = rand::thread_rng();
 
board.shuffle(&mut rng);
if !Self::is_valid(board) {
// random swap
let i = rng.gen_range(0, ..16);
let mut j = rng.gen_range(0, ..16);
while j == i {
j = rng.gen_range(0, ..16);
}
board.swap(i, j);
}
 
Self { board }
}
 
fn is_valid(mut board: Board) -> bool {
// TODO: optimize
let mut permutations = 0;
 
let pos = board.iter().position(|&cell| cell == Cell::Empty).unwrap();
 
if pos != 15 {
board.swap(pos, 15);
permutations += 1;
}
 
for i in 1..16 {
let pos = board
.iter()
.position(|&cell| match matches!(cell, Cell::Card(value) if value == {i))
Cell::Card(value) if value == i => true,
_ => false,
})
.unwrap();
 
if pos + 1 != i {
board.swap(pos, i - 1);
Line 10,894 ⟶ 13,252:
}
}
 
permutations % 2 == 0
}
 
fn get_empty_position(&self) -> usize {
self.board.iter().position(|&c| c == Cell::Empty).unwrap()
}
 
fn get_moves(&self) -> HashMap<Direction, Cell> {
let mut moves = HashMap::new();
let i = self.get_empty_position();
 
if i > 3 {
moves.insert(Direction::Up, self.board[i - 4]);
Line 10,920 ⟶ 13,278:
moves
}
 
fn play(&mut self, direction: &Direction) {
let i = self.get_empty_position();
Line 10,931 ⟶ 13,289:
};
}
 
fn is_complete(&self) -> bool {
self.board.iter().enumerate().all(|(i, &cell)| match cell {
Line 10,939 ⟶ 13,297:
}
}
 
impl fmt::Display for P15 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
r#try!(writewriteln!(f, "+----+----+----+----+\n"))?;
for (i, &cell) in self.board.iter().enumerate() {
match cell {
Cell::Card(value) => r#try!(write!(f, "| {value:2} ", value))?,
Cell::Empty => r#try!(write!(f, "| "))?,
}
 
if i % 4 == 3 {
r#try!(writewriteln!(f, "|\n"))?;
r#try!(writewriteln!(f, "+----+----+----+----+\n"))?;
}
}
Line 10,957 ⟶ 13,315:
}
}
 
fn main() {
let mut p15 = P15::new();
 
for turns in 1.. {
println!("{p15}", p15);
match ask_action(&p15.get_moves(), true) {
Action::Move(direction) => {
p15.play(&direction);
clear().expect("failed to clear screen");
}
Action::Quit => {
println!clear().expect("Byefailed to clear !screen");
 
println!("Bye!");
break;
}
}
 
if p15.is_complete() {
println!("Well done ! You won in {turns} turns", turns);
break;
}
}
}
 
fn ask_action(moves: &HashMap<Direction, Cell>, render_list: bool) -> Action {
use std::io::{self, Write};
use Action::*;
use Direction::*;
 
if render_list {
println!("Possible moves:");
println!("Possible moves:");
 
if let Some(&Cell::Card(value)) = moves.get(&Up) {
println!if let Some("\tU&Cell::Card(value)) {}",= valuemoves.get(&Up); {
println!("\tU) {value}");
}
if let Some(&Cell::Card(value)) = moves.get(&Left) {
println!("\tL) {value}");
}
if let Some(&Cell::Card(value)) = moves.get(&Right) {
println!("\tR) {value}");
}
if let Some(&Cell::Card(value)) = moves.get(&Down) {
println!("\tD) {value}");
}
println!("\tQ) Quit");
print!("Choose your move: ");
io::stdout().flush().unwrap();
} else {
print!("Unknown move, try again: ");
io::stdout().flush().unwrap();
}
 
if let Some(&Cell::Card(value)) = moves.get(&Left) {
println!("\tL) {}", value);
}
if let Some(&Cell::Card(value)) = moves.get(&Right) {
println!("\tR) {}", value);
}
if let Some(&Cell::Card(value)) = moves.get(&Down) {
println!("\tD) {}", value);
}
println!("\tQ) Quit");
print!("Choose your move : ");
io::stdout().flush().unwrap();
let mut action = String::new();
io::stdin().read_line(&mut action).expect("read error");
Line 11,012 ⟶ 13,377:
"Q" => Quit,
_ => {
println!if unknown_action("Unknown action:) {}", action);
ask_action(moves, true)
} else {
ask_action(moves, false)
}
}
}
}
}</lang>
 
fn unknown_action() -> bool {
use crossterm::{
cursor::MoveToPreviousLine,
terminal::{Clear, ClearType},
ExecutableCommand,
};
std::io::stdout().execute(MoveToPreviousLine(1)).unwrap();
std::io::stdout()
.execute(Clear(ClearType::CurrentLine))
.unwrap();
false
}
 
</syntaxhighlight>
 
=={{header|Scala}}==
<langsyntaxhighlight lang="scala">import java.util.Random
 
import jline.console._
Line 11,095 ⟶ 13,478:
.foldLeft(Board.solveState) { case (state, mv) => state.move(mv) }
}
}</langsyntaxhighlight>
 
=={{header|Scheme}}==
 
<langsyntaxhighlight lang="scheme">
(import (scheme base)
(scheme read)
Line 11,185 ⟶ 13,568:
 
(play-game)
</syntaxhighlight>
</lang>
 
{{out}}
Line 11,219 ⟶ 13,602:
=={{header|Scilab}}==
 
<syntaxhighlight lang="text">tiles=[1:15,0];
solution=[tiles(1:4);...
tiles(5:8);...
Line 11,302 ⟶ 13,685:
end
 
disp("Solved in "+string(n_moves)+" moves.");</langsyntaxhighlight>
 
{{out}}
Line 11,373 ⟶ 13,756:
 
=={{header|Simula}}==
<langsyntaxhighlight lang="simula">
BEGIN
CLASS FIFTEENPUZZLE(NUMTILES, SIDE, WIDTH, SEED);
Line 11,586 ⟶ 13,969:
P.REQUEST;
P.PRINTBOARD;
END.</langsyntaxhighlight>
{{out}}
<pre>INPUT RANDOM SEED:
Line 11,628 ⟶ 14,011:
{{works with|SML/NJ}}
{{works with|Moscow ML}}
<langsyntaxhighlight lang="sml">
(* Load required Modules for Moscow ML *)
load "Int";
Line 11,879 ⟶ 14,262:
 
val () = Console.start()
</syntaxhighlight>
</lang>
 
<b>Note:</b>
Line 11,896 ⟶ 14,279:
The window-title is used to show messages.
 
<langsyntaxhighlight lang="tcl"> # 15puzzle_21.tcl - HaJo Gurt - 2016-02-16
# http://wiki.tcl.tk/14403
 
Line 12,033 ⟶ 14,416:
 
# For some more versions, see: http://wiki.tcl.tk/15067 : Classic 15 Puzzle and http://wiki.tcl.tk/15085 : N-Puzzle
</syntaxhighlight>
</lang>
 
=={{header|UNIX Shell}}==
{{works with|Bourne Again SHell|4+}}
This plays the game in an ANSI-compliant terminal using vi/nethack movement keys to slide the tiles.
 
<syntaxhighlight lang="bash">#!/usr/bin/env bash
main() {
local puzzle=({1..15} " ") blank moves_kv i key count total last
printf '\n'
show_puzzle "${puzzle[@]}"
printf '\nPress return to scramble.'
read _
IFS= readarray -t puzzle < <(scramble "${puzzle[@]}")
printf '\r\e[A\e[A\e[A\e[A\e[A\e[A'
show_puzzle "${puzzle[@]}"
printf '\nUse hjkl to slide tiles. '
printf '\r\e[A\e[A\e[A\e[A\e[A'
total=0
while ! solved "${puzzle[@]}"; do
show_puzzle "${puzzle[@]}"
{ read blank; readarray -t moves_kv; } < <(find_moves "${puzzle[@]}")
local count=${#moves_kv[@]}/2
local -A moves
for (( i=0; i<count; i++ )); do
moves[${moves_kv[i]}]=${moves_kv[i+count]}
done
read -r -n 1 key
printf '\r '
if [[ "$key" = u ]]; then
(( total -= 2 ))
case "$last" in
h) key=l;;
j) key=k;;
k) key=j;;
l) key=h;;
esac
fi
if [[ -n "${moves[$key]}" ]]; then
last=$key
(( total++ ))
i=${moves[$key]}
puzzle[$blank]=${puzzle[i]}
puzzle[i]=' '
fi
printf '\r\e[A\e[A\e[A\e[A'
done
show_puzzle "${puzzle[@]}"
printf '\nSolved in %d moves. \n' "$total"
}
 
 
solved() {
local solved=({1..15} ' ')
[[ "${puzzle[*]}" == "${solved[*]}" ]]
}
 
show_puzzle() {
printf '%2s %2s %2s %2s\n' "$@"
}
 
find_moves() {
local puzzle=("$@")
local i j blank
for (( i=0; i<${#puzzle[@]}; ++i )); do
if [[ "${puzzle[i]}" == " " ]]; then
blank=$i
break
fi
done
local -A moves=()
if (( blank%4 )); then
# swapping blank with tile to its left
# is sliding that tile right, which is the l key
moves['l']=$(( blank-1 ))
fi
if (( blank%4 != 3 )); then
moves['h']=$(( blank+1 )) # left
fi
if (( blank >= 4 )); then
moves['j']=$(( blank-4 )) # down
fi
if (( blank < 12 )); then
moves['k']=$(( blank+4 )) # up
fi
printf '%s\n' "$blank" "${!moves[@]}" "${moves[@]}"
}
 
scramble() {
local puzzle=("$@") i j
for (( i=0; i<256; ++i )); do
local blank moves
{ read blank; readarray -t moves; } < <(find_moves "${puzzle[@]}")
moves=(${moves[@]:${#moves[@]}/2})
local dir=$(( RANDOM % ${#moves[@]} ))
j=${moves[dir]}
puzzle[blank]=${puzzle[j]}
puzzle[j]=' '
done
printf '%s\n' "${puzzle[@]}"
}
 
main "$@"</syntaxhighlight>
 
{{Out}}
<pre> 1 2 3 4
5 6 7 8
9 10 11 12
13 14 15
 
Press return to scramble.
</pre>
 
=={{header|VBA}}==
Line 12,042 ⟶ 14,536:
The last move is displayed on the input box, or an error message if an invalid move is attempted. When the puzzle is solved, the move count is displayed.
 
<syntaxhighlight lang="vb">
<lang vb>
Public iSide As Integer
Public iSize As Integer
Line 12,223 ⟶ 14,717:
ShowGrid = s
End Function
</syntaxhighlight>
</lang>
 
Sample output:
Line 12,238 ⟶ 14,732:
 
=={{header|Visual Basic .NET}}==
<langsyntaxhighlight lang="vbnet">Public Class Board
Inherits System.Windows.Forms.Form
 
Line 12,333 ⟶ 14,827:
End Function
 
End Class</langsyntaxhighlight>
 
=={{header|Visual Prolog}}==
Line 12,340 ⟶ 14,834:
[https://youtu.be/rWy3AX5HjXM 15 Puzzle in Visual Prolog - video]
 
<syntaxhighlight lang="visual prolog">
<lang Visual Prolog>
/* ------------------------------------------------------------------------------------------------------
 
Line 13,275 ⟶ 15,769:
% end of automatic code
end implement playDialog
</syntaxhighlight>
</lang>
 
=={{header|VBScript}}==
<syntaxhighlight lang="vb">
<lang VB>
'----------------15 game-------------------------------------
'WARNING: this script uses ANSI escape codes to position items on console so
Line 13,387 ⟶ 15,881:
usr= move(d)
end function
</syntaxhighlight>
</lang>
=={{header|Wren}}==
{{trans|Go}}
Line 13,393 ⟶ 15,887:
{{libheader|Wren-ioutil}}
{{libheader|Wren-fmt}}
<langsyntaxhighlight ecmascriptlang="wren">import "random" for Random
import "./dynamic" for Enum
import "./ioutil" for Input
import "./fmt" for Fmt
 
var Move = Enum.create("Move", ["up", "down", "right", "left"])
Line 13,513 ⟶ 16,007:
 
var p = Puzzle.new()
p.play()</langsyntaxhighlight>
 
{{out}}
Line 13,575 ⟶ 16,069:
 
=={{header|x86-64 Assembly}}==
<langsyntaxhighlight lang="assembly"> ; Puzzle15 by grosged (march 2019)
; How to play ?.. Just press one of the arrow keys then [enter] to valid
; ( press [Ctrl+C] to escape )
Line 13,656 ⟶ 16,150:
xchg ax,word[puzzle+4+rbx*4]
mov word[puzzle+4+rdx*4],ax
ret</langsyntaxhighlight>
 
=={{header|XBasic}}==
{{trans|Liberty BASIC}}
{{works with|Windows XBasic}}
<langsyntaxhighlight lang="xbasic">
PROGRAM "fifteenpuzzlegame"
VERSION "0.0001"
Line 13,817 ⟶ 16,311:
 
END PROGRAM
</syntaxhighlight>
</lang>
 
=={{header|XPL0}}==
<langsyntaxhighlight XPL0lang="xpl0">int Box, Hole, I;
[Box:= [^ ,^F,^E,^D, \starting configuration
^C,^B,^A,^9, \slide digits into ascending order
Line 13,850 ⟶ 16,344:
other []; \ignore 0 scan code prefix etc.
];
]</langsyntaxhighlight>
 
{{out}}
Line 13,861 ⟶ 16,355:
 
=={{header|Yabasic}}==
<langsyntaxhighlight Yabasiclang="yabasic">dx = 4 : dy = 4 : dxy = dx * dy
dim grid(dx, dy)
 
Line 13,938 ⟶ 16,432:
next y
next x
end sub</langsyntaxhighlight>
 
Adaptation from Phix solution
<langsyntaxhighlight Yabasiclang="yabasic">board$ = "123456789ABCDEF0"
solve$ = board$
pos = 16
Line 13,983 ⟶ 16,477:
loop
print "solved!\n"
</syntaxhighlight>
</lang>
 
=={{header|Zig}}==
<syntaxhighlight lang="zig">
const std = @import("std");
const stdout = std.io.getStdOut().writer();
 
const tokens = [16][]const u8{ " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", "10", "11", "12", "13", "14", "15", " " };
 
const empty_token = 15;
var empty: u8 = empty_token;
var cells: [16]u8 = undefined;
var invalid: bool = false;
 
const Move = enum { no, up, down, left, right };
 
fn showBoard() !void {
try stdout.writeAll("\n");
 
var solved = true;
var i: u8 = 0;
while (i < 16) : (i += 1) {
try stdout.print(" {s} ", .{tokens[cells[i]]});
if ((i + 1) % 4 == 0) try stdout.writeAll("\n");
if (i != cells[i]) solved = false;
}
 
try stdout.writeAll("\n");
 
if (solved) {
try stdout.writeAll("\n\n** You did it! **\n\n");
std.posix.exit(0);
}
}
 
fn updateToken(move: Move) !void {
const newEmpty = switch (move) {
Move.up => if (empty / 4 < 3) empty + 4 else empty,
Move.down => if (empty / 4 > 0) empty - 4 else empty,
Move.left => if (empty % 4 < 3) empty + 1 else empty,
Move.right => if (empty % 4 > 0) empty - 1 else empty,
else => empty,
};
 
if (empty == newEmpty) {
invalid = true;
} else {
invalid = false;
cells[empty] = cells[newEmpty];
cells[newEmpty] = empty_token;
empty = newEmpty;
}
}
 
fn waitForMove() !Move {
const reader = std.io.getStdIn().reader();
if (invalid) try stdout.writeAll("(invalid) ");
try stdout.writeAll("enter u/d/l/r or q: ");
const input = try reader.readBytesNoEof(2);
switch (std.ascii.toLower(input[0])) {
'q' => std.posix.exit(0),
'u' => return Move.up,
'd' => return Move.down,
'l' => return Move.left,
'r' => return Move.right,
else => return Move.no,
}
}
 
fn shuffle(moves: u8) !void {
var random = std.rand.DefaultPrng.init(@intCast(std.time.microTimestamp()));
const rand = random.random();
var n: u8 = 0;
while (n < moves) {
const move: Move = rand.enumValue(Move);
try updateToken(move);
if (!invalid) n += 1;
}
invalid = false;
}
 
pub fn main() !void {
var n: u8 = 0;
while (n < 16) : (n += 1) {
cells[n] = n;
}
 
try shuffle(50);
try showBoard();
 
while (true) {
try updateToken(try waitForMove());
try showBoard();
}
}
</syntaxhighlight>
 
{{out}}
<pre>
1 2 3 4
5 6 7 8
9 10 12
13 14 11 15
 
enter u/d/l/r or q: r
 
1 2 3 4
5 6 7 8
9 10 12
13 14 11 15
 
enter u/d/l/r or q: u
 
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15
 
enter u/d/l/r or q: l
 
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15
 
 
** You did it! **
</pre>
20

edits