15 puzzle game: Difference between revisions
m
Back to previous version, because of better understanding.
Puppydrum64 (talk | contribs) 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}}
<
position = 0
[Int = String] items
Line 140:
I g.game_over()
print(‘You WON’)
L.break</
{{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.)
<
;Ram Variables
Cursor_X equ $00FF0000 ;Ram for Cursor Xpos
Cursor_Y equ $00FF0000+1 ;Ram for Cursor Ypos
joypad1 equ $00FF0002
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</
{{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">
/* 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>
=={{header|Action!}}==
<
DEFINE X0="13"
DEFINE Y0="6"
Line 1,649 ⟶ 1,647:
FI
OD
RETURN</
{{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.
<
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;</
The package implementation is as follows.
<
Field: array(Row_Type, Col_Type) of Natural;
Line 1,732 ⟶ 1,730:
end loop;
end;
end Generic_Puzzle;</
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.
<
Ada.Numerics.Discrete_Random, Ada.Command_Line;
Line 1,807 ⟶ 1,805:
end;
end loop;
end Puzzle_15;</
{{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. <
=={{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}}
<
⍺←4 4
(s∨.<0)∨2≠⍴s←⍺:'invalid shape:'s
Line 1,877 ⟶ 2,121:
(1↓⍺)∇ n
}z
}</
{{out}}
<pre> fpg 10
Line 1,926 ⟶ 2,170:
=={{header|ARM Assembly}}==
{{works with|as|Raspberry Pi}}
<syntaxhighlight lang="arm assembly">
/* ARM assembly Raspberry PI */
Line 2,564 ⟶ 2,808:
bx lr @return
</syntaxhighlight>
=={{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}}==
<
fun mainframe(puz):
Line 2,681 ⟶ 3,155:
print 'You WON'
break
</syntaxhighlight>
=={{header|AutoHotkey}}==
<
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
}</
=={{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}}===
<
20 REM COMMODORE BASIC 2.0
30 REM ********************************
Line 2,879 ⟶ 3,451:
1130 FOR T=0 TO 400
1140 NEXT
1150 RETURN</
=={{header|BBC BASIC}}==
{{works with|BBC BASIC for Windows}}{{works with|ARM BBC BASIC}}{{works with|Brandy BASIC|Matrix Brandy}}
<
SIZE=4 : DIFFICULTY=3
Line 2,927 ⟶ 3,499:
COLOUR 0 : COLOUR 143
PRINT
ENDPROC</
=={{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.
<
#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;}</
===C89, short version, TTY mode===
<syntaxhighlight lang="c">/*
* RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC
*/
Line 3,079 ⟶ 3,727:
}
</syntaxhighlight>
{{Out}}
<pre>
Line 3,111 ⟶ 3,759:
===C89, long version, TTY/Winapi/ncurses modes===
<
* RosettaCode: Fifteen puzle game, C89, MS Windows Console API, MVC
*
Line 3,421 ⟶ 4,069:
return EXIT_SUCCESS;
}
</syntaxhighlight>
=={{header|C sharp|C#}}==
{{libheader|System.Windows.Forms}}
{{libheader|System.Drawing}}
{{works with|C sharp|
<
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
public class FifteenPuzzle
{
const int
const
private List<Button> Puzzles = new List<Button>();
private int Moves = 0;
private DateTime Start;
public class Puzzle
{
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
{
}
private Form
{
int ButtonMargin = 3;
int
Font ButtonFont = new Font("Arial", 15.75F, FontStyle.Regular);
Button StartButton = new
StartButton.Font = new Font("Arial", 9.75F, FontStyle.Regular);
StartButton.UseVisualStyleBackColor = true;
StartButton.Click
int FormWidth = (GridSize * ButtonSize) + ((GridSize - 1) * ButtonMargin) + (FormEdge * 2);
int FormHeigth = FormWidth
Form Form = new
Form.ClientSize = new Size(FormWidth,
Form.FormBorderStyle = FormBorderStyle.FixedSingle;
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);
}
return Form;
}
private void
{
do
}
}
for (int i = 0; i < Puzzles.Count; i++)
{
}
Moves = 0;
Start = DateTime.Now;
}
private void MovePuzzle(object Sender, EventArgs E)
{
Puzzle
Button Bt2 =
Puzzle
Moves++;
}
}
{
Button WrongPuzzle = Puzzles.Find(Bt => !((Puzzle)Bt.Tag).IsTruePlace);
if (UWin)
{
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));
}
}
private void Swap(
{
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;
}
}</
=={{header|C++}}==
<
#include <time.h>
#include <stdlib.h>
Line 3,684 ⟶ 4,426:
p15 p; p.play(); return 0;
}
</syntaxhighlight>
<pre>
+----+----+----+----+
Line 3,702 ⟶ 4,444:
Tested with GnuCOBOL
<
*> This code is dedicated to the public domain
*> This is GNUCOBOL 2.0
Line 3,852 ⟶ 4,594:
end-evaluate
.
end program fifteen.</
{{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
<
(: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!~%"))</
=={{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.
<syntaxhighlight>
sys topleft
background 432
textsize 13
len f[] 16
clear
for i
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 +
if h < 10
move x + 6 y +
.
color 885
text h
.
.
.
global done .
move x y
color 000
move
circle s / 3
move x + 3.5 y - 3.5
linewidth s / 3
curve [ x - s y + s x y + 2 * s x + s y + s ]
.
proc init . .
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
.
textsize 12
draw
.
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]
.
if f[i] > f[i + 1]
done = 1
.
on mouse_down
if done =
elif done = 3
.
.
on timer
if done = 1
smiley
done = 2
timer 2
else
done = 3
.
.
</syntaxhighlight>
=={{header|F_Sharp|F#}}==
<
// 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>
3 uses:
15 game with no parameters will generate a random game which may be solved.
Line 4,111 ⟶ 5,353:
</pre>
=={{header|Factor}}==
<
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</
{{out}}
<pre>
Line 4,222 ⟶ 5,464:
See [[15_puzzle_solver#Forth]] for a solver based on the same code.
<
cell 8 <> [if] s" 64-bit system required" exception throw [then]
Line 4,291 ⟶ 5,533:
solution 1000 shuffle play
</syntaxhighlight>
=={{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 <
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.</
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}}==
<
dim as string outstr = ""
for i as ubyte = 0 to 15
Line 4,527 ⟶ 5,769:
print "Congratulations! You win!"
print
drawboard B()</
=={{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}}==
<
'Inspiration came from: -
''http://rosettacode.org/wiki/15_Puzzle_Game
Line 4,828 ⟶ 6,168:
End
</syntaxhighlight>
[http://www.cogier.com/gambas/Copuzzle.png Click here for image of game in play]
=={{header|Go}}==
<
import (
Line 4,986 ⟶ 6,326:
return
}
}</
=={{header|Harbour}}==
<syntaxhighlight lang="harbour">
#include "inkey.ch"
Line 5,174 ⟶ 6,514:
SetColor(" W/N, BG+/B")
RETURN fim
</syntaxhighlight>
=={{header|Haskell}}==
<
import System.Random
Line 5,249 ⟶ 6,589:
randomIndex <- randomRIO (0, length moves - 1)
let move = moves !! randomIndex
shufflePuzzle (numOfShuffels - 1) (applyMove move puzzle)</
Output:
<pre>Please enter the difficulty level: 0, 1 or 2
Line 5,279 ⟶ 6,619:
Implementation:
<
genboard=:3 :0
Line 5,343 ⟶ 6,683:
showboard board
echo 'You win.'
)</
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:
<
15 puzzle
h for help, q to quit
Line 5,386 ⟶ 6,726:
│13│14│15│ │
└──┴──┴──┴──┘
You win.</
=={{header|Java}}==
{{works with|Java|8}}
<
import java.awt.*;
Line 5,603 ⟶ 6,943:
});
}
}</
=={{header|Javascript}}==
Play it [http://paulo-jorente.de/webgames/15p/ here]
<
var board, zx, zy, clicks, possibles, clickCounter, oldzx = -1, oldzy = -1;
function getPossibles() {
Line 5,726 ⟶ 7,066:
restart();
}
</syntaxhighlight>
Html to test
<pre>
Line 5,745 ⟶ 7,085:
=={{header|Julia}}==
<
using Random
Line 5,853 ⟶ 7,193:
puzzle15play()
</
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}}
<
import java.awt.BorderLayout
Line 6,021 ⟶ 7,467:
}
}
}</
=={{header|Liberty BASIC}}==
{{trans|Commodore BASIC}}
{{works with|Just BASIC}}
<syntaxhighlight lang="lb">
' 15-PUZZLE GAME
' ********************************
Line 6,160 ⟶ 7,606:
isPuzzleComplete = pc
end function
</syntaxhighlight>
=={{header|LiveCode}}==
<syntaxhighlight 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>
Screenshot:
[https://s24.postimg.org/uc6fx7kph/Livecode15_Puzzle_Game.png]
=={{header|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>
{{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">
Module Puzzle15 {
Line 6,472 ⟶ 7,918:
}
Puzzle15
</syntaxhighlight>
=={{header|Mathematica}} / {{header|Wolfram Language}}==
<
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]</
=={{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:
<
:- 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).</
As used:
<
:- interface.
:- import_module io.
Line 6,678 ⟶ 8,124:
"
Seed = time(NULL);
").</
=={{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}}==
<
type
Line 6,869 ⟶ 8,657:
draw board
echo "You win!"</
{{out}}
<pre>┌──┬──┬──┬──┐
Line 6,883 ⟶ 8,671:
=={{header|OCaml}}==
<
struct
type t = int array
Line 6,921 ⟶ 8,709:
print_string " > ";
Puzzle.move p (read_line () |> int_of_string)
done</
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">
program fifteen;
{$mode objfpc}
Line 7,261 ⟶ 9,049:
Run;
end.
</syntaxhighlight>
=={{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]
<
use strict;
Line 7,521 ⟶ 9,309:
</syntaxhighlight>
===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.
<
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>
=={{header|Phix}}==
=== console only ===
Kept simple. Obviously, increase the 5 random moves for more of a challenge.
<!--
<syntaxhighlight lang="phix">
constant ESC=27, UP=328, LEFT=331, RIGHT=333, DOWN=336
sequence board = tagset(15)&0, solve = board
integer pos = 16
procedure print_board()
for i=1 to length(board) do
puts(1,iff(i=pos?" ":sprintf("%3d",{board[i]})))
if mod(i,4)=0 then puts(1,"\n") end if
end for
puts(1,"\n")
end procedure
procedure move(integer d)
integer new_pos = pos+{+4,+1,-1,-4}[d]
if new_pos>=1 and new_pos<=16
and (mod(pos,4)=mod(new_pos,4) -- same col, or row:
or floor((pos-1)/4)=floor((new_pos-1)/4)) then
{board[pos],board[new_pos]} = {board[new_pos],0}
pos = new_pos
end if
end procedure
for i=1 to 5 do move(rand(4)) end for
while 1 do
print_board()
if board=solve then exit end if
integer c = find(wait_key(),{ESC,UP,LEFT,RIGHT,DOWN})-1
if c=0 then exit end if
move(c)
end while
puts(1,"solved!\n")
</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].
<!--<
<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>
<!--</
=={{header|PHP}}==
OOP and MVC design pattern has been used to facilitate any improvements, e.g. tiles with graphics instead of just numbers.
<
// Puzzle 15 Game - Rosseta Code - PHP 7 as the server-side script language.
Line 7,982 ⟶ 9,772:
?></p>
</body>
</html></
=={{header|Picat}}==
<syntaxhighlight lang="picat">
import util.
Line 8,050 ⟶ 9,840:
Board[R0,C0] := S,
Board[R,C] := 0.
</syntaxhighlight>
=={{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">
#15 Puzzle Game
$Script:Neighbours = @{
Line 8,317 ⟶ 10,107:
# Show Window
$result = Show-WPFWindow -Window $window
</syntaxhighlight>
=={{header|Processing}}==
<
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:
}
}
}</
'''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}}
<
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)</
=={{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">
#difficulty=10 ;higher is harder
#up="u"
Line 8,626 ⟶ 10,526:
Print("Won !")
CloseConsole()
</syntaxhighlight>
<pre>
1234
Line 8,640 ⟶ 10,540:
{{works with|Python|3.X}}
'''unoptimized'''
<
''' Structural Game for 15 - Puzzle with different difficulty levels'''
from random import randint
Line 8,765 ⟶ 10,665:
print('You WON')
break
</syntaxhighlight>
<pre>
Enter the difficulty : 0 1 2
Line 8,799 ⟶ 10,699:
===Python: using tkinter===
<
''' Python 3.6.5 code using Tkinter graphical user interface.'''
Line 9,049 ⟶ 10,949:
g = Game(root)
root.mainloop()
</syntaxhighlight>
=={{header|QB64}}==
<syntaxhighlight lang="qb64">
_TITLE "GUI Sliding Blocks Game "
RANDOMIZE TIMER
Line 9,136 ⟶ 11,036:
END IF
LOOP
</syntaxhighlight>
=={{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">
( moves: 0 - up, 1 - down, 2 - left, 3 - right )
Line 9,230 ⟶ 11,130:
15-puzzle
</syntaxhighlight>
=={{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">
puz15<-function(scramble.length=100){
m=matrix(c(1:15,0),byrow=T,ncol=4)
Line 9,323 ⟶ 11,223:
}
}
</syntaxhighlight>
Sample output:
Line 9,370 ⟶ 11,270:
It uses the <code>2htdp/universe</code> package.
<
(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))</
=={{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"
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';
}</
Sample screen shot:
<pre>
Line 9,557 ⟶ 11,457:
=={{header|Rebol}}==
<
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</
=={{header|Red}}==
<syntaxhighlight lang="red">
Red [Needs: 'view]
system/view/screens/1/pane/-1/visible?: false ;; suppress console window
Line 9,631 ⟶ 11,531:
view lay
</syntaxhighlight>
=={{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).
<
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</
{{out|output|text= when using the default input:}}
<pre>
Line 9,749 ⟶ 11,649:
=={{header|Ring}}==
<
# Project : CalmoSoft Fifteen Puzzle Game
# Date : 2018/12/01
Line 10,516 ⟶ 12,416:
return
</syntaxhighlight>
'''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}}==
<
class Board
Line 10,612 ⟶ 12,971:
end
Board.new</
{{out}}
Line 10,632 ⟶ 12,991:
{{libheader|JRubyArt}}
Gui Version:-
<
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>
=={{header|Run BASIC}}==
<
' ---- 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</
Output:
[[File:KokengeGame15.jpg]]
Line 10,812 ⟶ 13,171:
=={{header|Rust}}==
{{libheader|rand}}
<syntaxhighlight lang
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;
#[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
let mut j = rng.gen_range(0
while j == i {
j = rng.gen_range(0
}
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|
.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 {
for (i, &cell) in self.board.iter().enumerate() {
match cell {
Cell::Card(value) =>
Cell::Empty =>
}
if i % 4 == 3 {
}
}
Line 10,957 ⟶ 13,315:
}
}
fn main() {
let mut p15 = P15::new();
for turns in 1.. {
println!("{p15}"
match ask_action(&p15.get_moves(), true) {
Action::Move(direction) => {
p15.play(&direction);
clear().expect("failed to clear screen");
}
Action::Quit => {
println!("Bye!");
break;
}
}
if p15.is_complete() {
println!("Well done
break;
}
}
}
fn ask_action(moves: &HashMap<Direction, Cell>, render_list: bool) -> Action {
use Action::*;
use Direction::*;
if render_list {
println!("Possible moves:");
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();
}
let mut action = String::new();
io::stdin().read_line(&mut action).expect("read error");
Line 11,012 ⟶ 13,377:
"Q" => Quit,
_ => {
ask_action(moves, true)
} else {
ask_action(moves, false)
}
}
}
}
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}}==
<
import jline.console._
Line 11,095 ⟶ 13,478:
.foldLeft(Board.solveState) { case (state, mv) => state.move(mv) }
}
}</
=={{header|Scheme}}==
<
(import (scheme base)
(scheme read)
Line 11,185 ⟶ 13,568:
(play-game)
</syntaxhighlight>
{{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.");</
{{out}}
Line 11,373 ⟶ 13,756:
=={{header|Simula}}==
<
BEGIN
CLASS FIFTEENPUZZLE(NUMTILES, SIDE, WIDTH, SEED);
Line 11,586 ⟶ 13,969:
P.REQUEST;
P.PRINTBOARD;
END.</
{{out}}
<pre>INPUT RANDOM SEED:
Line 11,628 ⟶ 14,011:
{{works with|SML/NJ}}
{{works with|Moscow ML}}
<
(* Load required Modules for Moscow ML *)
load "Int";
Line 11,879 ⟶ 14,262:
val () = Console.start()
</syntaxhighlight>
<b>Note:</b>
Line 11,896 ⟶ 14,279:
The window-title is used to show messages.
<
# 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>
=={{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">
Public iSide As Integer
Public iSize As Integer
Line 12,223 ⟶ 14,717:
ShowGrid = s
End Function
</syntaxhighlight>
Sample output:
Line 12,238 ⟶ 14,732:
=={{header|Visual Basic .NET}}==
<
Inherits System.Windows.Forms.Form
Line 12,333 ⟶ 14,827:
End Function
End Class</
=={{header|Visual Prolog}}==
Line 12,340 ⟶ 14,834:
[https://youtu.be/rWy3AX5HjXM 15 Puzzle in Visual Prolog - video]
<syntaxhighlight lang="visual prolog">
/* ------------------------------------------------------------------------------------------------------
Line 13,275 ⟶ 15,769:
% end of automatic code
end implement playDialog
</syntaxhighlight>
=={{header|VBScript}}==
<syntaxhighlight 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>
=={{header|Wren}}==
{{trans|Go}}
Line 13,393 ⟶ 15,887:
{{libheader|Wren-ioutil}}
{{libheader|Wren-fmt}}
<
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()</
{{out}}
Line 13,575 ⟶ 16,069:
=={{header|x86-64 Assembly}}==
<
; 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</
=={{header|XBasic}}==
{{trans|Liberty BASIC}}
{{works with|Windows XBasic}}
<
PROGRAM "fifteenpuzzlegame"
VERSION "0.0001"
Line 13,817 ⟶ 16,311:
END PROGRAM
</syntaxhighlight>
=={{header|XPL0}}==
<
[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.
];
]</
{{out}}
Line 13,861 ⟶ 16,355:
=={{header|Yabasic}}==
<
dim grid(dx, dy)
Line 13,938 ⟶ 16,432:
next y
next x
end sub</
Adaptation from Phix solution
<
solve$ = board$
pos = 16
Line 13,983 ⟶ 16,477:
loop
print "solved!\n"
</syntaxhighlight>
=={{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>
|