I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)

# 15 puzzle game

15 puzzle game
You are encouraged to solve this task according to the task description, using any language you may know.

Implement the Fifteen Puzzle Game.

The   15-puzzle   is also known as:

•   Fifteen Puzzle
•   Gem Puzzle
•   Boss Puzzle
•   Game of Fifteen
•   Mystic Square
•   14-15 Puzzle
•   and some others.

## 11l

`T Puzzle   position = 0   [Int = String] items    F main_frame()      V& d = .items      print(‘+-----+-----+-----+-----+’)      print(‘|#.|#.|#.|#.|’.format(d[1], d[2], d[3], d[4]))      print(‘+-----+-----+-----+-----+’)      print(‘|#.|#.|#.|#.|’.format(d[5], d[6], d[7], d[8]))      print(‘+-----+-----+-----+-----+’)      print(‘|#.|#.|#.|#.|’.format(d[9], d[10], d[11], d[12]))      print(‘+-----+-----+-----+-----+’)      print(‘|#.|#.|#.|#.|’.format(d[13], d[14], d[15], d[16]))      print(‘+-----+-----+-----+-----+’)    F format(=ch)      ch = ch.trim(‘ ’)      I ch.len == 1         R ‘  ’ch‘  ’      E I ch.len == 2         R ‘  ’ch‘ ’      E         assert(ch.empty)         R ‘     ’    F change(=to)      V fro = .position      L(a, b) .items         I b == .format(String(to))            to = a            L.break      swap(&.items[fro], &.items[to])      .position = to    F build_board(difficulty)      L(i) 1..16         .items[i] = .format(String(i))      V tmp = 0      L(a, b) .items         I b == ‘  16 ’            .items[a] = ‘     ’            tmp = a            L.break      .position = tmp      Int diff      I difficulty == 0         diff = 10      E I difficulty == 1         diff = 50      E         diff = 100      L 0 .< diff         V lst = .valid_moves()         [Int] lst1         L(j) lst            lst1.append(Int(j.trim(‘ ’)))         .change(lst1[random:(lst1.len)])    F valid_moves()      V pos = .position      I pos C [6, 7, 10, 11]         R [.items[pos - 4], .items[pos - 1], .items[pos + 1], .items[pos + 4]]      E I pos C [5, 9]         R [.items[pos - 4], .items[pos + 4], .items[pos + 1]]      E I pos C [8, 12]         R [.items[pos - 4], .items[pos + 4], .items[pos - 1]]      E I pos C [2, 3]         R [.items[pos - 1], .items[pos + 1], .items[pos + 4]]      E I pos C [14, 15]         R [.items[pos - 1], .items[pos + 1], .items[pos - 4]]      E I pos == 1         R [.items[pos + 1], .items[pos + 4]]      E I pos == 4         R [.items[pos - 1], .items[pos + 4]]      E I pos == 13         R [.items[pos + 1], .items[pos - 4]]      E         assert(pos == 16)         R [.items[pos - 1], .items[pos - 4]]    F game_over()      V flag = 0B      L(a, b) .items         I b != ‘     ’            I a == Int(b.trim(‘ ’))               flag = 1B            E               flag = 0B      R flag V g = Puzzle()g.build_board(Int(input("Enter the difficulty : 0 1 2\n2 => highest 0 => lowest\n")))g.main_frame()print(‘Enter 0 to exit’)L   print("Hello user:\nTo change the position just enter the no. near it")   V lst = g.valid_moves()   [Int] lst1   L(i) lst      lst1.append(Int(i.trim(‘ ’)))      print(i.trim(‘ ’)" \t", end' ‘’)   print()   V x = Int(input())   I x == 0      L.break   E I x !C lst1      print(‘Wrong move’)   E      g.change(x)   g.main_frame()   I g.game_over()      print(‘You WON’)      L.break`
Output:

The same as in Python.

## AArch64 Assembly

Works with: as version Raspberry Pi 3B version Buster 64 bits
` /* ARM assembly AARCH64 Raspberry PI 3B *//*  program puzzle15_64.s   */ /*******************************************//* Constantes file                         *//*******************************************//* for this file see task include a file in language AArch64 assembly*/.include "../includeConstantesARM64.inc" .equ NBBOX,  16.equ GRAINE,  123456      // change for other game.equ NBSHUFFLE, 4         .equ KEYSIZE,  8 .equ IOCTL,     0x1D  // Linux syscall.equ SIGACTION, 0x86   // Linux syscall.equ SYSPOLL,   0x16  // Linux syscall.equ CREATPOLL, 0x14  // Linux syscall.equ CTLPOLL,   0x15  // Linux syscall .equ TCGETS,    0x5401.equ TCSETS,    0x5402.equ ICANON,    2.equ ECHO,     10.equ POLLIN,    1.equ EPOLL_CTL_ADD,    1 .equ SIGINT,   2    // Issued if the user sends an interrupt signal (Ctrl + C).equ SIGQUIT,  3    // Issued if the user sends a quit signal (Ctrl + D).equ SIGTERM, 15    // Software termination signal (sent by kill by default).equ SIGTTOU, 22    //  /*******************************************//* Structures                               *//********************************************//* structure termios see doc linux*/    .struct  0term_c_iflag:                    // input modes    .struct  term_c_iflag + 4 term_c_oflag:                    // output modes    .struct  term_c_oflag + 4 term_c_cflag:                    // control modes    .struct  term_c_cflag + 4 term_c_lflag:                    // local modes    .struct  term_c_lflag + 4 term_c_cc:                       // special characters    .struct  term_c_cc + 20      // see length if necessary term_fin: /* structure sigaction see doc linux */    .struct  0sa_handler:    .struct  sa_handler + 8sa_mask:    .struct  sa_mask + 8sa_flags:    .struct  sa_flags + 8sa_sigaction:    .struct  sa_sigaction + 8sa_fin: /* structure poll see doc linux */    .struct  0poll_event:    .struct  poll_event + 8poll_fd:                            //   File Descriptor    .struct  poll_fd + 8 poll_fin:/*********************************//* Initialized data              *//*********************************/.datasMessResult:           .ascii " "sMessValeur:           .fill 11, 1, ' '             // size => 11szCarriageReturn:      .asciz "\n"szMessGameWin:         .asciz "You win in @ move number !!!!\n"szMessMoveError:       .asciz "Huh... Impossible move !!!!\n"szMessErreur:          .asciz "Error detected.\n"szMessErrInitTerm:     .asciz "Error terminal init.\n"szMessErrInitPoll:     .asciz "Error poll init.\n"szMessErreurKey:       .asciz "Error read key.\n"szMessSpaces:          .asciz "    "qGraine:               .quad GRAINEszMessErr:             .asciz    "Error code hexa : @ décimal : @ \n" szClear:     .byte 0x1B              .byte 'c'                         // console clear             .byte 0/*********************************//* UnInitialized data            *//*********************************/.bss.align 4sZoneConv:      .skip 24qCodeError:     .skip 8ibox:           .skip 4 * NBBOX                // game boxesqEnd:           .skip 8                        // 0 loop  1 = end loopqTouche:        .skip KEYSIZE                  // value key pressedstOldtio:       .skip term_fin                 // old terminal statestCurtio:       .skip term_fin                 // current terminal statestSigAction:    .skip sa_fin                   // area signal structurestSigAction1:   .skip sa_finstSigAction2:   .skip sa_finstSigAction3:   .skip sa_finstPoll1:        .skip poll_fin                 // area poll structurestPoll2:        .skip poll_finstevents:       .skip 16/*********************************//*  code section                 *//*********************************/.text.global main main:                                // entry of program     mov x0,#0    bl initTerm                      // terminal init    cmp x0,0                         // error ?    blt 100f    bl initPoll                      // epoll instance init    cmp x0,0    blt 99f    mov x22,x0                       // save epfd    ldr x2,qAdribox    mov x9,#0                        // init counter     mov x0,01:                                   // loop init boxs    add x1,x0,#1                     // box value    str w1,[x2,x0, lsl #2]           // store value    add x0,x0,#1                     // increment counter    cmp x0,#NBBOX - 2                // end ?    ble 1b    mov x10,#15                      // empty box location     ldr x0,qAdribox    bl shuffleGame2:                                   // loop moves    ldr x0,qAdribox    bl displayGame3:    mov x0,x22                       // epfd    bl waitKey    cmp x0,0    beq 3b                           // no ket pressed -> loop    blt 99f                          // error ?    bl readKey                       // read key     cmp x0,#-1    beq 99f                          // error      cmp x0,3                         // <ctrl_C>    beq 5f    cmp x0,113                       // saisie q (quit) ?    beq 5f    cmp x0,81                        // saisie Q  (Quit)?    beq 5f    mov x1,x0                        // key    ldr x0,qAdribox    bl keyMove                       // analyze key move     ldr x0,qAdribox    bl gameOK                        // end game ?    cmp x0,#1    bne 2b                           // no -> loop                                     // win    mov x0,x9                        // move counter    ldr x1,qAdrsZoneConv    bl conversion10    ldr x0,qAdrszMessGameWin    ldr x1,qAdrsZoneConv    bl strInsertAtCharInc            // insert result at @ character    bl affichageMess5:    bl restauTerm                    // terminal restaur    mov x0, #0                       // return code    b 100f99:    bl restauTerm                    // terminal restaur    mov x0,1                         // return code error    b 100f100:                                 // standard end of the program     mov x8, #EXIT                    // request to exit program    svc #0                           // perform the system call qAdrsMessValeur:          .quad sMessValeurqAdrszCarriageReturn:     .quad szCarriageReturnqAdrsMessResult:          .quad sMessResultqAdribox:                 .quad iboxqAdrszMessGameWin:        .quad szMessGameWinqAdrstevents:             .quad steventsqAdrszMessErreur:         .quad szMessErreurqAdrstOldtio:             .quad stOldtioqAdrstCurtio:             .quad stCurtioqAdrstSigAction:          .quad stSigActionqAdrstSigAction1:         .quad stSigAction1qAdrSIG_IGN:              .quad 1qAdrqEnd:                 .quad qEndqAdrqTouche:              .quad qToucheqAdrszMessErrInitTerm:    .quad szMessErrInitTermqAdrszMessErrInitPoll:    .quad szMessErrInitPollqAdrszMessErreurKey:      .quad szMessErreurKey/******************************************************************//*     key move                                                   */ /******************************************************************//* x0 contains boxs address           *//* x1 contains key value               *//* x9 move counter                     *//* x10 contains location empty box    */keyMove:    stp x1,lr,[sp,-16]!              // save  registers    mov x7,x0    lsr x1,x1,16               cmp x1,#0x42                     // down arrow     bne 1f    cmp x10,#4                       // if x10 < 4   error    blt 80f    sub x2,x10,#4                    // compute location    b 90f1:    cmp x1,#0x41                     // high arrow    bne 2f    cmp x10,#11                      // if x10 > 11   error    bgt 80f    add x2,x10,#4                    // compute location    b 90f2:    cmp x1,#0x43                     // right arrow    bne 3f    tst x10,#0b11                    // if x10 = 0,4,8,12   error    beq 80f    sub x2,x10,#1                    // compute location    b 90f3:    cmp x1,#0x44                     // left arrow    bne 100f    and x3,x10,#0b11                 // error if x10 = 3 7 11 and 15    cmp x3,#3    beq 80f    add x2,x10,#1                    // compute location    b 90f 80:                                  // move error    ldr x0,qAdrqCodeError    mov x1,#1    str x1,[x0]    b 100f90:                                  // white box and move box inversion    ldr w3,[x7,x2,lsl #2]    str w3,[x7,x10,lsl #2]    mov x10,x2    mov x3,#0    str w3,[x7,x10,lsl #2]    add x9,x9,#1                        // increment move counter100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30qAdrqCodeError:             .quad qCodeError/******************************************************************//*     shuffle game                                       */ /******************************************************************//* x0 contains boxs address           */shuffleGame:    stp x1,lr,[sp,-16]!            // save  registers    stp x2,x3,[sp,-16]!            // save  registers    stp x4,x5,[sp,-16]!            // save  registers    mov x1,x0    mov x0,NBSHUFFLE    bl genereraleas    lsl x4,x0,#11:    mov x0,#14    bl genereraleas    add x3,x0,#1    mov x0,#14    bl genereraleas    add x5,x0,#1    ldr w2,[x1,x3,lsl #2]    ldr w0,[x1,x5,lsl #2]    str w2,[x1,x5,lsl #2]    str w0,[x1,x3,lsl #2]    subs x4,x4,#1    bgt 1b 100:    ldp x4,x5,[sp],16              // restaur  2 registers    ldp x2,x3,[sp],16              // restaur  2 registers    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/******************************************************************//*     game Ok ?                                      */ /******************************************************************//* x0 contains boxs address           */gameOK:    stp x1,lr,[sp,-16]!            // save  registers    stp x2,x3,[sp,-16]!            // save  registers    mov x2,#0    ldr w3,[x0,x2,lsl #2]    add x2,x2,#11:    ldr w1,[x0,x2,lsl #2]    cmp w1,w3    bge 2f    mov x0,#0                      // game not Ok    b 100f2:    mov x3,x1    add x2,x2,#1    cmp x2,#NBBOX -2    ble 1b    mov x0,#1                      // game Ok 100:    ldp x2,x3,[sp],16              // restaur  2 registers    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/******************************************************************//*     display game                                       */ /******************************************************************//* x0 contains boxs address           */displayGame:    stp x1,lr,[sp,-16]!            // save  registers                                   // clear screen !    mov x4,x0    ldr x0,qAdrszClear    bl affichageMess     mov x2,#0    ldr x1,qAdrsMessValeur1:    ldr w0,[x4,x2,lsl #2]    cmp w0,#0    bne 2f    ldr w0,iSpaces                    // store spaces    str w0,[x1]    b 3f2:    bl conversion10                     // call conversion decimal    cmp x0,1    beq 21f    mov x0,0x20    strh w0,[x1,#2]    b 3f21:    mov w0,0x2020    str w0,[x1,#1]3:    ldr x0,qAdrsMessResult    bl affichageMess                    // display message    add x0,x2,#1    tst x0,#0b11    bne 4f    ldr x0,qAdrszCarriageReturn    bl affichageMess                    // display message4:    add x2,x2,#1    cmp x2,#NBBOX - 1    ble 1b    ldr x0,qAdrszCarriageReturn    bl affichageMess                    // display line return    ldr x0,qAdrqCodeError               // error detected ?    ldr x1,[x0]    cmp x1,#0    beq 100f    mov x1,#0                           // raz error code    str x1,[x0]    ldr x0,qAdrszMessMoveError          // display error message    bl affichageMess100:    ldp x1,lr,[sp],16                   // restaur  2 registers    ret                                 // return to address lr x30iSpaces:              .int 0x00202020   // spacesqAdrszClear:          .quad szClear          qAdrszMessMoveError:  .quad szMessMoveError /***************************************************//*   Generation random number                  *//***************************************************//* x0 contains limit  */genereraleas:    stp x1,lr,[sp,-16]!            // save  registers    stp x2,x3,[sp,-16]!            // save  registers    ldr x1,qAdrqGraine    ldr x2,[x1]    ldr x3,qNbDep1    mul x2,x3,x2    ldr x3,qNbDep2    add x2,x2,x3    str x2,[x1]                    // maj de la graine pour l appel suivant     cmp x0,#0    beq 100f    udiv x3,x2,x0    msub x0,x3,x0,x2               // résult = remainder 100:                               // end function    ldp x2,x3,[sp],16              // restaur  2 registers    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*****************************************************/qAdrqGraine: .quad qGraineqNbDep1:     .quad 0x0019660dqNbDep2:     .quad 0x3c6ef35f /******************************************************************//*     traitement du signal                                       */ /******************************************************************/sighandler:    stp x0,lr,[sp,-16]!            // save  registers    str x1,[sp,-16]!     ldr x0,qAdrqEnd    mov x1,#1                      // maj zone end    str x1,[x0]    ldr x1,[sp],16    ldp x0,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/***************************************************//*   display error message                         *//***************************************************//* x0 contains error code  x1 : message address */displayError:    stp x2,lr,[sp,-16]!            // save  registers    mov x2,x0                      // save error code    mov x0,x1    bl affichageMess    mov x0,x2                      // error code    ldr x1,qAdrsZoneConv    bl conversion16                // conversion hexa    ldr x0,qAdrszMessErr           // display error message    ldr x1,qAdrsZoneConv    bl strInsertAtCharInc               // insert result at @ character    mov x3,x0    mov x0,x2                      // error code    ldr x1,qAdrsZoneConv               // result address    bl conversion10S                // conversion decimale    mov x0,x3    ldr x1,qAdrsZoneConv    bl strInsertAtCharInc               // insert result at @ character    bl affichageMess100:    ldp x2,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30qAdrszMessErr:                 .quad szMessErrqAdrsZoneConv:                 .quad sZoneConv/*********************************//* init terminal state            *//*********************************/initTerm:    stp x1,lr,[sp,-16]!            // save  registers    /* read terminal state */    mov x0,STDIN                   // input console    mov x1,TCGETS    ldr x2,qAdrstOldtio    mov x8,IOCTL                   // call system Linux    svc 0     cbnz x0,98f                    // error ?     adr x0,sighandler              // adresse routine traitement signal    ldr x1,qAdrstSigAction         // adresse structure sigaction    str x0,[x1,sa_handler]         // maj handler    mov x0,SIGINT                  // signal type    ldr x1,qAdrstSigAction    mov x2,0    mov x3,8    mov x8,SIGACTION               // call system    svc 0      cmp x0,0                       // error ?    bne 98f    mov x0,SIGQUIT    ldr x1,qAdrstSigAction    mov x2,0                       // NULL    mov x8,SIGACTION               // call system     svc 0     cmp x0,0                       // error ?    bne 98f    mov x0,SIGTERM    ldr x1,qAdrstSigAction    mov x2,0                       // NULL    mov x8,SIGACTION               // appel systeme     svc 0     cmp x0,0    bne 98f    //    adr x0,qAdrSIG_IGN             // address signal igonre function    ldr x1,qAdrstSigAction1    str x0,[x1,sa_handler]    mov x0,SIGTTOU                 //invalidate other process signal    ldr x1,qAdrstSigAction1    mov x2,0                       // NULL    mov x8,SIGACTION               // call system     svc 0     cmp x0,0    bne 98f    //    /* read terminal current state  */    mov x0,STDIN    mov x1,TCGETS    ldr x2,qAdrstCurtio            // address current termio    mov x8,IOCTL                   // call systeme     svc 0     cmp x0,0                       // error ?    bne 98f    mov x2,ICANON | ECHO           // no key pressed echo on display    mvn x2,x2                      // and one key     ldr x1,qAdrstCurtio    ldr x3,[x1,#term_c_lflag]    and x3,x2,x2                   // add flags     str x3,[x1,#term_c_lflag]      // and store    mov x0,STDIN                   // maj terminal current state     mov x1,TCSETS    ldr x2,qAdrstCurtio    mov x8,IOCTL                   // call system    svc 0     cbz x0,100f98:                                // error display    ldr x1,qAdrszMessErrInitTerm    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30qAdrstSigAction2:    .quad stSigAction2qAdrstSigAction3:    .quad stSigAction3/*********************************//* init instance epool            *//*********************************/initPoll:    stp x1,lr,[sp,-16]!            // save  registers    ldr x0,qAdrstevents    mov x1,STDIN                   // maj structure events    str x1,[x0,#poll_fd]           // maj FD    mov x1,POLLIN                  // action code    str x1,[x0,#poll_event]    mov x0,0    mov x8,CREATPOLL               // create epoll instance    svc 0    cmp x0,0                       // error ?    ble 98f    mov x10,x0                     // return FD epoll instance    mov x1,EPOLL_CTL_ADD    mov x2,STDIN                   // Fd to we want add    ldr x3,qAdrstevents            // structure events address    mov x8,CTLPOLL                 // call system control epoll    svc 0    cmp x0,0                       // error ?    blt 98f                       // no    mov x0,x10                     // return FD epoll instance    b 100f98:                                // error display    ldr x1,qAdrszMessErrInitPoll   // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* wait key                      *//*********************************//* x0 contains FD poll    */waitKey:    stp x1,lr,[sp,-16]!            // save  registers    ldr x11,qAdrqTouche            // key address    str xzr,[x11]                  // raz key1:    ldr x1,qAdrqEnd                // if signal ctrl-c  -> end    ldr x1,[x1]    cbnz x1,100f     ldr x1,qAdrstevents    mov x2,12                      // size events    mov x3,1                       // timeout = 1  TODO: ??    mov x4,0    mov x8,SYSPOLL                 // call system wait POLL    svc 0     cmp x0,0                       // key pressed ?    bge 100f98:                                // error display    ldr x1,qAdrszMessErreurKey        // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* read key                      *//*********************************//* x0 returns key value */readKey:    stp x1,lr,[sp,-16]!            // save  registers    mov x0,STDIN                   // File Descriptor    ldr x1,qAdrqTouche             // buffer address    mov x2,KEYSIZE                 // key size    mov x8,READ                    // read key    svc #0    cmp x0,0                       // error ?    ble 98f    ldr x2,qAdrqTouche             // key address    ldr x0,[x2]    b 100f98:                                // error display    ldr x1,qAdrszMessErreur        // error message    bl   displayError    mov x0,-1100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30/*********************************//* restaur terminal state        *//*********************************/restauTerm:    stp x1,lr,[sp,-16]!            // save  registers    mov x0,STDIN                   // end then restaur begin state terminal    mov x1,TCSETS    ldr x2,qAdrstOldtio    mov x8,IOCTL                   // call system      svc 0    cbz x0,100f    ldr x1,qAdrszMessErreur        // error message    bl   displayError100:    ldp x1,lr,[sp],16              // restaur  2 registers    ret                            // return to address lr x30 /********************************************************//*        File Include fonctions                        *//********************************************************//* for this file see task include a file in language AArch64 assembly */.include "../includeARM64.inc" `

## Action!

`DEFINE BOARDSIZE="16"DEFINE X0="13"DEFINE Y0="6"DEFINE ITEMW="3"DEFINE ITEMH="2" BYTE ARRAY board(BOARDSIZE)BYTE emptyX,emptyY,solved,first=[1] BYTE FUNC Index(BYTE x,y)RETURN (x+y*4) PROC UpdateItem(BYTE x,y)  BYTE item   Position(X0+x*ITEMW+1,Y0+y*ITEMH+1)  item=board(Index(x,y))  IF item=0 THEN    Print("  ")  ELSEIF item<10 THEN    Put(160) Put(item+176)  ELSE    Put(item/10+176)    Put(item MOD 10+176)  FIRETURN PROC UpdateBoard()  BYTE x,y   FOR y=0 TO 3  DO    FOR x=0 TO 3    DO      UpdateItem(x,y)         OD  ODRETURN PROC DrawGrid()  CHAR ARRAY    top=[13 17 18 18 23 18 18 23 18 18 23 18 18 5],    row=[13 124 32 32 124 32 32 124 32 32 124 32 32 124],    mid=[13 1 18 18 19 18 18 19 18 18 19 18 18 4],    bot=[13 26 18 18 24 18 18 24 18 18 24 18 18 3]  BYTE y,i   y=Y0  Position(X0,y) Print(top) y==+1  Position(X0,y) Print(row) y==+1  FOR i=0 TO 2  DO    Position(X0,y) Print(mid) y==+1    Position(X0,y) Print(row) y==+1  OD  Position(X0,y) Print(bot)RETURN PROC DrawBoard()  DrawGrid()  UpdateBoard()RETURN PROC FindEmpty()  BYTE i   FOR i=0 TO BOARDSIZE-1  DO    IF board(i)=0 THEN      emptyX=i MOD 4      emptyY=i/4    FI  ODRETURN PROC Wait(BYTE frames)  BYTE RTCLOK=\$14  frames==+RTCLOK  WHILE frames#RTCLOK DO ODRETURN PROC UpdateStatus()  Position(9,3) Print("Game status: ")  IF solved THEN    Print("SOLVED !")    IF first=0 THEN      Sound(0,100,10,5) Wait(5)      Sound(0,60,10,5) Wait(5)      Sound(0,40,10,5) Wait(5)      Sound(0,0,0,0)    FI    first=0  ELSE    Print("Shuffled")  FIRETURN PROC InitBoard()  BYTE i   FOR i=1 TO BOARDSIZE  DO    board(i-1)=i MOD 16  OD  FindEmpty()  solved=1  UpdateStatus()RETURN BYTE FUNC IsSolved()  BYTE i   FOR i=1 TO BOARDSIZE  DO    IF board(i-1)#i MOD 16 THEN      RETURN (0)    FI  ODRETURN (1) PROC CheckStatus()  BYTE tmp   tmp=IsSolved()  IF solved#tmp THEN    solved=tmp    UpdateStatus()  FIRETURN PROC Swap(BYTE x1,y1,x2,y2)  BYTE tmp,i1,i2   i1=Index(x1,y1)  i2=Index(x2,y2)  tmp=board(i1)  board(i1)=board(i2)  board(i2)=tmp  UpdateItem(x1,y1)  UpdateItem(x2,y2)  CheckStatus()RETURN PROC Shuffle()  BYTE i,j,tmp   i=BOARDSIZE-1  WHILE i>0  DO    j=Rand(i)    tmp=board(i)    board(i)=board(j)    board(j)=tmp    i==-1  OD  FindEmpty()  UpdateBoard()  CheckStatus()RETURN PROC MoveLeft()  IF emptyX=0 THEN RETURN FI  Swap(emptyX,emptyY,emptyX-1,emptyY)  emptyX==-1RETURN PROC MoveRight()  IF emptyX=3 THEN RETURN FI  Swap(emptyX,emptyY,emptyX+1,emptyY)  emptyX==+1RETURN PROC MoveUp()  IF emptyY=0 THEN RETURN FI  Swap(emptyX,emptyY,emptyX,emptyY-1)  emptyY==-1RETURN PROC MoveDown()  IF emptyY=3 THEN RETURN FI  Swap(emptyX,emptyY,emptyX,emptyY+1)  emptyY==+1RETURN PROC Main()  BYTE k,lastStick=[255],currStick,    CH=\$02FC, ;Internal hardware value for last key pressed    CRSINH=\$02F0 ;Controls visibility of cursor   Graphics(0)  SetColor(2,0,2)  CRSINH=1 ;hide cursor  Position(10,18) Print("Joystick - move tiles")  Position(9,19) Print("Space bar - shuffle")  Position(15,20) Print("ESC - exit")  InitBoard()  DrawBoard()  DO    currStick=Stick(0)    IF currStick#lastStick THEN      IF currStick=11 THEN MoveRight()      ELSEIF currStick=7 THEN MoveLeft()      ELSEIF currStick=13 THEN MoveUp()      ELSEIF currStick=14 THEN MoveDown()      FI    FI    lastStick=currStick    k=CH    IF k#\$FF THEN CH=\$FF FI    IF k=33 THEN Shuffle()    ELSEIF k=28 THEN EXIT    FI  ODRETURN`
Output:

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.

`generic   Rows, Cols: Positive;   with function Name(N: Natural) return String; -- with Pre => (N < Rows*Cols);   -- Name(0) shall return the name for the empty tilepackage Generic_Puzzle is    subtype Row_Type is Positive range 1 .. Rows;   subtype Col_Type is Positive range 1 .. Cols;   type Moves is (Up, Down, Left, Right);   type Move_Arr is array(Moves) of Boolean;    function Get_Point(Row: Row_Type; Col: Col_Type) return String;   function Possible return Move_Arr;   procedure Move(The_Move: Moves); end Generic_Puzzle;`

The package implementation is as follows.

`package body Generic_Puzzle is    Field: array(Row_Type, Col_Type) of Natural;   Current_R: Row_Type := Rows;   Current_C: Col_Type := Cols;   -- invariant: Field(Current_R, Current_C=0)    -- and for all R, C: Field(R, C) < R*C   -- and for all (R, C) /= (RR, CC): Field(R, C) /= Field(RR, CC)    function Get_Point(Row: Row_Type; Col: Col_Type) return String is      (Name(Field(Row, Col)));    function Possible return Move_Arr is      (Up => Current_R > 1, Down => Current_R < Rows,       Left => Current_C > 1, Right => Current_C < Cols);    procedure Move(The_Move: Moves) is      Old_R: Row_Type; Old_C: Col_Type; N: Natural;   begin      if not Possible(The_Move) then	 raise Constraint_Error with "attempt to make impossible move";      else	 -- remember current row and column	 Old_R := Current_R;	 Old_C := Current_C; 	 -- move the virtual cursor to a new position	 case The_Move is 	   when Up    => Current_R := Current_R - 1;	   when Down  => Current_R := Current_R + 1;	   when Left  => Current_C := Current_C - 1;	   when Right => Current_C := Current_C + 1;	 end case; 	 -- swap the tiles on the board	 N := Field(Old_R, Old_C);	 Field(Old_R, Old_C) := Field(Current_R, Current_C);	 Field(Current_R, Current_C) := N;      end if;   end Move; begin   declare   -- set field to its basic setting      N: Positive := 1;   begin      for R in Row_Type loop	 for C in Col_Type loop	    if (R /= Current_R) or else (C /= Current_C) then 	       Field(R, C) := N;	       N := N + 1;	    else	       Field(R, C) := 0;	    end if;	 end loop;      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.

`with Generic_Puzzle, Ada.Text_IO,      Ada.Numerics.Discrete_Random, Ada.Command_Line; procedure Puzzle_15 is    function Image(N: Natural) return String is      (if N=0 then "   " elsif N < 10 then " " & Integer'Image(N)	else Integer'Image(N));    package Puzzle is new Generic_Puzzle(Rows => 4, Cols => 4, Name => Image);    package Rnd is new Ada.Numerics.Discrete_Random(Puzzle.Moves);   Rand_Gen: Rnd.Generator;    Level: Natural := (if Ada.Command_Line.Argument_Count = 0 then 10                       else Natural'Value(Ada.Command_Line.Argument(1)));   Initial_Moves: Natural := (2**(Level/2) + 2**((1+Level)/2))/2;   Texts: constant array(Puzzle.Moves) of String(1..9) :=       ("u,U,^,8: ", "d,D,v,2: ", "l,L,<,4: ", "r,R,>,6: ");   Move_Counter: Natural := 0;       Command: Character;  begin    -- randomize board    for I in 1 .. Initial_Moves loop       declare	  M: Puzzle.Moves := Rnd.Random(Rand_Gen);       begin	  if Puzzle.Possible(M) then	     Puzzle.Move(M);	  end if;       end;    end loop;     -- read command and perform move	      loop      -- Print board      for R in Puzzle.Row_Type loop	 for C in Puzzle.Col_Type loop	    Ada.Text_IO.Put(Puzzle.Get_Point(R, C));	 end loop;	 Ada.Text_IO.New_Line;      end loop;      Ada.Text_IO.Get(Command);      begin	 case Command is	    when 'u' | 'U' | '^' | '8' =>	       Ada.Text_IO.Put_Line("Up!"); Puzzle.Move(Puzzle.Up);	    when 'd' | 'D' | 'v' | '2' =>	       Ada.Text_IO.Put_Line("Down!"); Puzzle.Move(Puzzle.Down);	    when 'l' | 'L' | '<' | '4' =>	       Ada.Text_IO.Put_Line("Left!"); Puzzle.Move(Puzzle.Left);	    when 'r' | 'R' | '>' | '6' =>	       Ada.Text_IO.Put_Line("Right!"); Puzzle.Move(Puzzle.Right);	    when '!' => 	       Ada.Text_IO.Put_Line(Natural'Image(Move_Counter) & " moves!"); 	       exit;	    when others => 	       raise Constraint_Error with "wrong input";	 end case;	 Move_Counter := Move_Counter + 1;      exception when Constraint_Error => 	 Ada.Text_IO.Put_Line("Possible Moves and Commands:");	 for M in Puzzle.Moves loop	    if Puzzle.Possible(M) then	       Ada.Text_IO.Put(Texts(M) & Puzzle.Moves'Image(M) & "   ");	    end if;	 end loop;	 Ada.Text_IO.Put_Line("!: Quit");      end;   end loop;end Puzzle_15;`
Output:
```>./puzzle_15 4
1  2  3  4
5  6  7  8
9 14 10 11
13    15 12
8
Up!
1  2  3  4
5  6  7  8
9    10 11
13 14 15 12
6
Right!
1  2  3  4
5  6  7  8
9 10    11
13 14 15 12
5
Possible Moves and Commands:
u,U,^,8: UP   d,D,v,2: DOWN   l,L,<,4: LEFT   r,R,>,6: RIGHT   !: Quit
1  2  3  4
5  6  7  8
9 10    11
13 14 15 12
6
Right!
1  2  3  4
5  6  7  8
9 10 11
13 14 15 12
2
Down!
1  2  3  4
5  6  7  8
9 10 11 12
13 14 15
!
4 moves!```
For other puzzles, one must just the single line with the package instantiation. E.g., for an 8-puzzle, we would write the following.
`   package Puzzle is new Generic_Puzzle(Rows => 3, Cols => 3, Name => Image);`

## APL

Works with: Dyalog APL version 16.0
`fpg←{⎕IO←0    ⍺←4 4    (s∨.<0)∨2≠⍴s←⍺:'invalid shape:'s    0≠⍴⍴⍵:'invalid shuffle count:'⍵    d←d,-d←↓2 2⍴3↑1    e←¯1+⍴c←'↑↓←→○'    b←w←s⍴w←1⌽⍳×/s    z←⊃{        z p←⍵        n←(?⍴p)⊃p←(p≡¨(⊂s)|p)/p←(d~p)+⊂z        b[z n]←b[n z]        -⍨\n z    }⍣⍵⊢(s-1)0    ⎕←b    ⍬{        b≡w:'win'        0=⍴⍺:⍞∇ ⍵        e=i←c⍳m←⊃⍺:'quit'        i>e:⍞∇ ⍵⊣⎕←'invalid direction:'m        n≢s|n←⍵+i⊃d:⍞∇ ⍵⊣'out of bounds:'m        b[⍵ n]←b[n ⍵]        ⎕←(s×0≠⍴⍺)⍴b        (1↓⍺)∇ n    }z}`
Output:
```      fpg 10
1  3  0  4
5  2  6  8
9 10  7 12
13 14 11 15
←
1  0  3  4
5  2  6  8
9 10  7 12
13 14 11 15
↓
1  2  3  4
5  0  6  8
9 10  7 12
13 14 11 15
→
1  2  3  4
5  6  0  8
9 10  7 12
13 14 11 15
↓↓
1  2  3  4
5  6  7  8
9 10 11 12
13 14  0 15
→
1  2  3  4
5  6  7  8
9 10 11 12
13 14 15  0
win

2 5 fpg 2
1 2 3 0 4
6 7 8 9 5
→
1 2 3 4 0
6 7 8 9 5
↓
1 2 3 4 5
6 7 8 9 0
win
```

## ARM Assembly

Works with: as version Raspberry Pi
`  /* ARM assembly Raspberry PI  *//*  program puzzle15.s   */ /************************************//* Constantes                       *//************************************/.equ STDIN,  0     @ Linux input console.equ STDOUT, 1     @ Linux output console.equ EXIT,   1     @ Linux syscall.equ READ,   3     @ Linux syscall.equ WRITE,  4     @ Linux syscall .equ IOCTL,     0x36  @ Linux syscall.equ SIGACTION, 0x43  @ Linux syscall.equ SYSPOLL,   0xA8  @ Linux syscall .equ TCGETS,    0x5401.equ TCSETS,    0x5402.equ ICANON,    2.equ ECHO,     10.equ POLLIN,    1 .equ SIGINT,   2    @ Issued if the user sends an interrupt signal (Ctrl + C).equ SIGQUIT,  3    @ Issued if the user sends a quit signal (Ctrl + D).equ SIGTERM, 15    @ Software termination signal (sent by kill by default).equ SIGTTOU, 22    @  .equ NBBOX,  16.equ TAILLEBUFFER,   10 /*******************************************//* Structures                               *//********************************************//* structure termios see doc linux*/    .struct  0term_c_iflag:                    @ input modes    .struct  term_c_iflag + 4 term_c_oflag:                    @ output modes    .struct  term_c_oflag + 4 term_c_cflag:                    @ control modes    .struct  term_c_cflag + 4 term_c_lflag:                    @ local modes    .struct  term_c_lflag + 4 term_c_cc:                       @ special characters    .struct  term_c_cc + 20      @ see length if necessary term_fin: /* structure sigaction see doc linux */    .struct  0sa_handler:    .struct  sa_handler + 4 sa_mask:    .struct  sa_mask + 4 sa_flags:    .struct  sa_flags + 4 sa_sigaction:    .struct  sa_sigaction + 4 sa_fin: /* structure poll see doc linux */    .struct  0poll_fd:                            @   File Descriptor    .struct  poll_fd + 4 poll_events:                        @  events mask    .struct  poll_events + 4 poll_revents:                       @ events returned    .struct  poll_revents + 4 poll_fin:/*********************************//* Initialized data              *//*********************************/.datasMessResult:           .ascii " "sMessValeur:           .fill 11, 1, ' '             @ size => 11szCarriageReturn:      .asciz "\n"szMessGameWin:         .ascii "You win in "sMessCounter:           .fill 11, 1, ' '            @ size => 11                       .asciz " move number !!!!\n"szMessMoveError:       .asciz "Huh... Impossible move !!!!\n"szMessErreur:          .asciz "Error detected.\n"szMessSpaces:          .asciz "    "iGraine:               .int 123456/*************************************************/szMessErr: .ascii	"Error code hexa : "sHexa: .space 9,' '         .ascii "  decimal :  "sDeci: .space 15,' '         .asciz "\n"szClear:     .byte 0x1B 		     .byte 'c'                         @ console clear		     .byte 0/*********************************//* UnInitialized data            *//*********************************/.bss.align 4iCodeError:     .skip 4ibox:          .skip 4 * NBBOX                 @ game boxesiEnd:           .skip 4                        @ 0 loop  1 = end loopiTouche:        .skip 4                        @ value key pressedstOldtio:       .skip term_fin                 @ old terminal statestCurtio:       .skip term_fin                 @ current terminal statestSigAction:    .skip sa_fin                   @ area signal structurestSigAction1:   .skip sa_finstPoll1:        .skip poll_fin                 @ area poll structurestPoll2:        .skip poll_fin/*********************************//*  code section                 *//*********************************/.text.global main main:                                @ entry of program     mov r0,#0    ldr r2,iAdribox    mov r9,#0                        @ move counter 1:                                   @ loop init boxs    add r1,r0,#1                     @ box value    str r1,[r2,r0, lsl #2]           @ store value    add r0,#1                        @ increment counter    cmp r0,#NBBOX - 2                @ end ?    ble 1b    mov r10,#15                      @ empty box location     ldr r0,iAdribox    bl shuffleGame2:                                   @ loop moves    ldr r0,iAdribox    bl displayGame    //ldr r0,iAdribox    //bl gameOK                      @ end game ?    //cmp r0,#1    //beq 50f    bl readKey                       @ read key     cmp r0,#-1    beq 100f                         @ error or control-c    mov r1,r0                        @ key    ldr r0,iAdribox    bl keyMove    ldr r0,iAdribox    bl gameOK                        @ end game ?    cmp r0,#1    bne 2b                           @ no -> loop50:                                  @ win    mov r0,r9                        @ move counter    ldr r1,iAdrsMessCounter    bl conversion10    ldr r0,iAdrszMessGameWin    bl affichageMess 100:                                 @ standard end of the program     mov r0, #0                       @ return code    mov r7, #EXIT                    @ request to exit program    svc #0                           @ perform the system call iAdrsMessValeur:          .int sMessValeuriAdrszCarriageReturn:     .int szCarriageReturniAdrsMessResult:          .int sMessResultiAdribox:                 .int iboxiAdrszMessGameWin:        .int szMessGameWiniAdrsMessCounter:         .int sMessCounter/******************************************************************//*     key move                                                   */ /******************************************************************//* r0 contains boxs address           *//* r1 contains key value               *//* r9 move counter                     *//* r10 contains location empty box    */keyMove:    push {r1-r8,lr}                  @ save  registers    mov r8,r0    cmp r1,#0x42                     @ down arrow     bne 1f    cmp r10,#4                       @ if r10 < 4   error    blt 80f    sub r2,r10,#4                    @ compute location    b 90f1:    cmp r1,#0x41                     @ high arrow    bne 2f    cmp r10,#11                      @ if r10 > 11   error    bgt 80f    add r2,r10,#4                    @ compute location    b 90f2:    cmp r1,#0x43                     @ right arrow    bne 3f    tst r10,#0b11                    @ if r10 = 0,4,8,12   error    beq 80f    sub r2,r10,#1                    @ compute location    b 90f3:    cmp r1,#0x44                     @ left arrow    bne 100f    and r3,r10,#0b11                 @ error if r10 = 3 7 11 and 15    cmp r3,#3    beq 80f    add r2,r10,#1                    @ compute location    b 90f 80:                                  @ move error    ldr r0,iAdriCodeError    mov r1,#1    str r1,[r0]    b 100f90:                                  @ white box and move box inversion    ldr r3,[r8,r2,lsl #2]    str r3,[r8,r10,lsl #2]    mov r10,r2    mov r3,#0    str r3,[r8,r10,lsl #2]    add r9,#1                        @ increment move counter100:    pop {r1-r8,lr}                   @ restaur registers     bx lr                            @returniAdriCodeError:             .int iCodeError/******************************************************************//*     shuffle game                                       */ /******************************************************************//* r0 contains boxs address           */shuffleGame:    push {r1-r6,lr}                     @ save  registers    mov r1,r0    mov r0,#4    bl genereraleas    lsl r4,r0,#1    mov r0,r81:    mov r0,#14    bl genereraleas    add r6,r0,#1    mov r0,#14    bl genereraleas    add r5,r0,#1    ldr r2,[r1,r6,lsl #2]    ldr r3,[r1,r5,lsl #2]    str r2,[r1,r5,lsl #2]    str r3,[r1,r6,lsl #2]    subs r4,#1    bgt 1b 100:    pop {r1-r6,lr}                      @ restaur registers     bx lr                               @return/******************************************************************//*     game Ok ?                                      */ /******************************************************************//* r0 contains boxs address           */gameOK:    push {r1-r8,lr}                     @ save  registers    mov r8,r0    mov r2,#0    ldr r3,[r8,r2,lsl #2]    add r2,#11:    ldr r4,[r8,r2,lsl #2]    cmp r4,r3    movlt r0,#0                         @ game mot Ok    blt 100f    mov r3,r4    add r2,#1    cmp r2,#NBBOX -2    ble 1b    mov r0,#1                           @ game Ok 100:    pop {r1-r8,lr}                      @ restaur registers     bx lr                               @return/******************************************************************//*     display game                                       */ /******************************************************************//* r0 contains boxs address           */displayGame:    push {r1-r5,lr}                     @ save  registers    @ clear !    mov r4,r0    ldr r0,iAdrszClear    bl affichageMess     mov r2,#0    ldr r1,iAdrsMessValeur1:    ldr r0,[r4,r2,lsl #2]    cmp r0,#0    ldreq r0,iSpaces                    @ store spaces    streq r0,[r1]    beq 2f    bl conversion10                     @ call conversion decimal    mov r0,#0    strb r0,[r1,#3]                     @ zéro final2:     ldr r0,iAdrsMessResult    bl affichageMess                    @ display message    add r0,r2,#1    tst r0,#0b11    bne 3f    ldr r0,iAdrszCarriageReturn    bl affichageMess                    @ display message3:    add r2,#1    cmp r2,#NBBOX - 1    ble 1b    ldr r0,iAdrszCarriageReturn    bl affichageMess                    @ display line return    ldr r0,iAdriCodeError               @ error detected ?    ldr r1,[r0]    cmp r1,#0    beq 100f    mov r1,#0                           @ raz error code    str r1,[r0]    ldr r0,iAdrszMessMoveError          @ display error message    bl affichageMess100:    pop {r1-r5,lr}                      @ restaur registers     bx lr                               @returniSpaces:                       .int 0x00202020       @ spacesiAdrszClear:                   .int szClear          iAdrszMessMoveError:           .int szMessMoveError/******************************************************************//*     display text with size calculation                         */ /******************************************************************//* r0 contains the address of the message */affichageMess:    push {r0,r1,r2,r7,lr}                          @ save  registres    mov r2,#0                                      @ counter length 1:                                                 @ loop length calculation     ldrb r1,[r0,r2]                                @ read octet start position + index     cmp r1,#0                                      @ if 0 its over     addne r2,r2,#1                                 @ else add 1 in the length     bne 1b                                         @ and loop                                                    @ so here r2 contains the length of the message     mov r1,r0                                      @ address message in r1     mov r0,#STDOUT                                 @ code to write to the standard output Linux     mov r7, #WRITE                                 @ code call system "write"     svc #0                                         @ call systeme     pop {r0,r1,r2,r7,lr}                           @ restaur des  2 registres */     bx lr                                          @ return  /******************************************************************//*     Converting a register to a decimal unsigned                */ /******************************************************************//* r0 contains value and r1 address area   *//* r0 return size of result (no zero final in area) *//* area size => 11 bytes          */.equ LGZONECAL,   10conversion10:    push {r1-r4,lr}                                 @ save registers     mov r3,r1    mov r2,#LGZONECAL1:                                                  @ start loop    bl divisionpar10U                               @ unsigned  r0 <- dividende. quotient ->r0 reste -> r1    add r1,#48                                      @ digit    strb r1,[r3,r2]                                 @ store digit on area    cmp r0,#0                                       @ stop if quotient = 0     subne r2,#1                                     @ else previous position    bne 1b                                          @ and loop                                                    @ and move digit from left of area    mov r4,#02:    ldrb r1,[r3,r2]    strb r1,[r3,r4]    add r2,#1    add r4,#1    cmp r2,#LGZONECAL    ble 2b                                                      @ and move spaces in end on area    mov r0,r4                                         @ result length     mov r1,#' '                                       @ space3:    strb r1,[r3,r4]                                   @ store space in area    add r4,#1                                         @ next position    cmp r4,#LGZONECAL    ble 3b                                            @ loop if r4 <= area size 100:    pop {r1-r4,lr}                                    @ restaur registres     bx lr                                             @return /***************************************************//*   division par 10   unsigned                    *//***************************************************//* r0 dividende   *//* r0 quotient    *//* r1 remainder   */divisionpar10U:    push {r2,r3,r4, lr}    mov r4,r0                                          @ save value    ldr r3,iMagicNumber                                @ r3 <- magic_number    raspberry 1 2    umull r1, r2, r3, r0                               @ r1<- Lower32Bits(r1*r0) r2<- Upper32Bits(r1*r0)     mov r0, r2, LSR #3                                 @ r2 <- r2 >> shift 3    add r2,r0,r0, lsl #2                               @ r2 <- r0 * 5     sub r1,r4,r2, lsl #1                               @ r1 <- r4 - (r2 * 2)  = r4 - (r0 * 10)    pop {r2,r3,r4,lr}    bx lr                                              @ leave function iMagicNumber:  	.int 0xCCCCCCCD/***************************************************//*   Generation random number                  *//***************************************************//* r0 contains limit  */genereraleas:    push {r1-r4,lr}                                    @ save registers     ldr r4,iAdriGraine    ldr r2,[r4]    ldr r3,iNbDep1    mul r2,r3,r2    ldr r3,iNbDep1    add r2,r2,r3    str r2,[r4]                                        @ maj de la graine pour l appel suivant     cmp r0,#0    beq 100f    mov r1,r0                                          @ divisor    mov r0,r2                                          @ dividende    bl division    mov r0,r3                                          @ résult = remainder 100:                                                   @ end function    pop {r1-r4,lr}                                     @ restaur registers    bx lr                                              @ return/*****************************************************/iAdriGraine: .int iGraineiNbDep1: .int 0x343FDiNbDep2: .int 0x269EC3 /***************************************************//* integer division unsigned                       *//***************************************************/division:    /* r0 contains dividend */    /* r1 contains divisor */    /* r2 returns quotient */    /* r3 returns remainder */    push {r4, lr}    mov r2, #0                                         @ init quotient    mov r3, #0                                         @ init remainder    mov r4, #32                                        @ init counter bits    b 2f1:                                                     @ loop     movs r0, r0, LSL #1                                @ r0 <- r0 << 1 updating cpsr (sets C if 31st bit of r0 was 1)    adc r3, r3, r3                                     @ r3 <- r3 + r3 + C. This is equivalent to r3 ? (r3 << 1) + C     cmp r3, r1                                         @ compute r3 - r1 and update cpsr     subhs r3, r3, r1                                   @ if r3 >= r1 (C=1) then r3 <- r3 - r1     adc r2, r2, r2                                     @ r2 <- r2 + r2 + C. This is equivalent to r2 <- (r2 << 1) + C 2:    subs r4, r4, #1                                    @ r4 <- r4 - 1     bpl 1b                                             @ if r4 >= 0 (N=0) then loop    pop {r4, lr}    bx lr/***************************************************//* read touch                                      *//***************************************************/readKey:    push {r1-r7,lr}    mov r5,#0    /* read terminal state */    mov r0,#STDIN                                @ input console    mov r1,#TCGETS    ldr r2,iAdrstOldtio    mov r7, #IOCTL                               @ call system Linux    svc #0     cmp r0,#0                                    @ error ?    beq 1f    ldr r1,iAdrszMessErreur                      @ error message    bl   displayError    mov r0,#-1    b 100f1:    adr r0,sighandler                            @ adresse routine traitement signal    ldr r1,iAdrstSigAction                       @ adresse structure sigaction    str r0,[r1,#sa_handler]                      @ maj handler    mov r0,#SIGINT                               @ signal type    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ call system    svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r0,#SIGQUIT    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ call system     svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r0,#SIGTERM    ldr r1,iAdrstSigAction    mov r2,#0                                    @ NULL    mov r7, #SIGACTION                           @ appel systeme     svc #0     cmp r0,#0    bne 97f    @    adr r0,iSIG_IGN                              @ address signal ignore function    ldr r1,iAdrstSigAction1    str r0,[r1,#sa_handler]    mov r0,#SIGTTOU                              @invalidate other process signal    ldr r1,iAdrstSigAction1    mov r2,#0                                    @ NULL    mov r7,#SIGACTION                            @ call system     svc #0     cmp r0,#0    bne 97f    @    /* read terminal current state  */    mov r0,#STDIN    mov r1,#TCGETS    ldr r2,iAdrstCurtio                          @ address current termio    mov r7,#IOCTL                                @ call systeme     svc #0     cmp r0,#0                                    @ error ?    bne 97f    mov r2,#ICANON | ECHO                        @ no key pressed echo on display    mvn r2,r2                                    @ and one key     ldr r1,iAdrstCurtio    ldr r3,[r1,#term_c_lflag]    and r3,r2                                    @ add flags     str r3,[r1,#term_c_lflag]                    @ and store    mov r0,#STDIN                                @ maj terminal current state     mov r1,#TCSETS    ldr r2,iAdrstCurtio    mov r7, #IOCTL                               @ call system    svc #0     cmp r0,#0    bne 97f    @2:                                               @ loop waiting key    ldr r0,iAdriEnd                              @ if signal ctrl-c  -> end    ldr r0,[r0]    cmp r0,#0    movne r5,#-1    bne 98f    ldr r0,iAdrstPoll1                            @ address structure poll    mov r1,#STDIN    str r1,[r0,#poll_fd]                          @ maj FD    mov r1,#POLLIN                                @ action code    str r1,[r0,#poll_events]    mov r1,#1                                     @ items number structure poll    mov r2,#0                                     @ timeout = 0     mov r7,#SYSPOLL                               @ call system POLL    svc #0     cmp r0,#0                                     @ key pressed ?    ble 2b                                        @ no key pressed -> loop                                                  @ read key    mov r0,#STDIN                                 @ File Descriptor    ldr r1,iAdriTouche                            @ buffer address    mov r2,#TAILLEBUFFER                          @ buffer size    mov r7,#READ                                  @ read key    svc #0    cmp r0,#0                                     @ error ?    bgt 98f 97:                                               @ error detected    ldr r1,iAdrszMessErreur                       @ error message    bl   displayError    mov r5,#-198:                                               @ end then restaur begin state terminal    mov r0,#STDIN    mov r1,#TCSETS    ldr r2,iAdrstOldtio    mov r7,#IOCTL                                 @ call system      svc #0    cmp r0,#0    beq 99f                                       @ restaur ok    ldr r1,iAdrszMessErreur                       @ error message    bl   displayError    mov r0,#-1    b 100f99:    cmp r5,#0                                     @ error or control-c    ldreq r2,iAdriTouche                          @ key address    ldreqb r0,[r2,#2]                             @ return key byte    movne r0,r5                                   @ or error100:    pop {r1-r7, lr}    bx lriSIG_IGN:                 .int 1iAdriEnd:                 .int iEndiAdrstPoll1:              .int stPoll1iAdriTouche:              .int iToucheiAdrstOldtio:             .int stOldtioiAdrstCurtio:             .int stCurtioiAdrstSigAction:          .int stSigActioniAdrstSigAction1:         .int stSigAction1iAdrszMessErreur :        .int szMessErreur /******************************************************************//*     traitement du signal                                       */ /******************************************************************/sighandler:    push {r0,r1}    ldr r0,iAdriEnd    mov r1,#1                 @ maj zone end    str r1,[r0]    pop {r0,r1}    bx lr/***************************************************//*   display error message                         *//***************************************************//* r0 contains error code  r1 : message address */displayError:    push {r0-r2,lr}                         @ save registers    mov r2,r0                               @ save error code    mov r0,r1    bl affichageMess    mov r0,r2                               @ error code    ldr r1,iAdrsHexa    bl conversion16                         @ conversion hexa    mov r0,r2                               @ error code    ldr r1,iAdrsDeci                        @ result address    bl conversion10                         @ conversion decimale    ldr r0,iAdrszMessErr                    @ display error message    bl affichageMess100:    pop {r0-r2,lr}                          @ restaur registers    bx lr                                   @ return iAdrszMessErr:                 .int szMessErriAdrsHexa:                     .int sHexaiAdrsDeci:                     .int sDeci/******************************************************************//*     Converting a register to hexadecimal                      */ /******************************************************************//* r0 contains value and r1 address area   */conversion16:    push {r1-r4,lr}                                    @ save registers    mov r2,#28                                         @ start bit position    mov r4,#0xF0000000                                 @ mask    mov r3,r0                                          @ save entry value1:                                                     @ start loop    and r0,r3,r4                                       @value register and mask    lsr r0,r2                                          @ move right     cmp r0,#10                                         @ compare value    addlt r0,#48                                       @ <10  ->digit	    addge r0,#55                                       @ >10  ->letter A-F    strb r0,[r1],#1                                    @ store digit on area and + 1 in area address    lsr r4,#4                                          @ shift mask 4 positions    subs r2,#4                                         @  counter bits - 4 <= zero  ?    bge 1b                                             @  no -> loop 100:    pop {r1-r4,lr}                                     @ restaur registers     bx lr                                              @return  `

## Astro

`type Puzzle(var items: {}, var position: -1) fun mainframe(puz):    let d = puz.items    print('+-----+-----+-----+-----+')    print(d[1], d[2], d[3], d[4], first: '|', sep: '|', last: '|')    print('+-----+-----+-----+-----+')    print(d[5], d[6], d[7], d[8], first: '|', sep: '|', last: '|')    print('+-----+-----+-----+-----+')    print(d[9], d[10], d[11], d[12], first: '|', sep: '|', last: '|')    print('+-----+-----+-----+-----+')    print(d[13], d[14], d[15], d[16], first: '|', sep: '|', last: '|')    print('+-----+-----+-----+-----+') fun format(puz, ch):    match ch.trim().length:        1 => '  \$ch  '        2 => '  \$ch '        0 => '     ' fun change(puz, to):    let fro = puz.position    for a, b in puz.items where b == puz.format(str i):        to = a        break     swap(puz.items[fro], :[to])    puz.position = to; fun buildboard(puz, difficulty):    for i in 1..16:        puz.items[i] = puz.format(str i)     var tmp = a    for a, b in puz.items where b == '  16 ':        puz.items[a] = '     '            tmp = a            break     puz.position = tmp    let diff = match difficulty:        0 => 10        1 => 50        _ => 100     for i in 1..diff:        let lst = puz.validmoves()        let lst1 = []        for j in lst:            lst1.push! j.trim().int()        puz.change(lst1[random(1, lst1.length - 1)]) fun validmoves(puz):    match puz.position:        6 | 7 | 10 | 11 =>            puz.items[pos - 4], :[pos - 1], :[pos + 1], :[pos + 4]        5 | 9 =>            puz.items[pos - 4], :[pos + 4], :[pos + 1]        8 | 12 =>            puz.items[pos - 4], :[pos + 4], :[pos - 1]        2 | 3 =>            puz.items[pos - 1], :[pos + 1], :[pos + 4]        14 | 15 =>            puz.items[pos - 1], :[pos + 1], :[pos - 4]        1 =>            puz.items[pos + 1], :[pos + 4]        4 =>            puz.items[pos - 1], :[pos + 4]        13 =>            puz.items[pos + 1], :[pos - 4]        16 =>            puz.items[pos - 1], :[pos - 4] fun mainframe(puz):    var flag = false    for a, b in puz.items:        if b == '     ':            pass        else:            flag = (a == b.trim().int())    ..    return flag let game = Puzzle()game.buildboard(    int(input('Enter the difficulty : 0 1 2\n2 => highest 0=> lowest\n')))game.mainframe() print 'Enter 0 to exit' loop:    print 'Hello user:\nTo change the position just enter the no. near it'     var lst = game.validmoves()    var lst1 = []    for i in lst:        lst1.push! i.trim().int()        print(i.strip(), '\t', last: '')     print()     let value = int(input())    if value == 0:        break    elif x not in lst1:        print('Wrong move')    else:        game.change(x)     game.mainframe()    if g.gameover():        print 'You WON'        break `

## AutoHotkey

`Size := 20Grid := [], Deltas := ["-1,0","1,0","0,-1","0,1"], Width := Size * 2.5Gui, font, S%Size%Gui, add, text, y1loop, 4{	Row := A_Index	loop, 4	{		Col := A_Index		Gui, add, button, % (Col=1 ? "xs y+1" : "x+1 yp") " v" Row "_" Col " w" Width " gButton -TabStop", % Grid[Row,Col] := Col + (Row-1)*4 ; 1-16	}}GuiControl, Hide, % Row "_" Col	; 4_4Gui, add, Button, % "xs gShuffle w" 4 * Width + 3, ShuffleGui, show,, 15 Puzzlereturn;------------------------------GuiClose:ExitAppreturn;------------------------------Shuffle:Shuffle := trueloop, 1000{	Random, Rnd, 1,4	Move(StrSplit(Deltas[Rnd], ",").1, StrSplit(Deltas[Rnd], ",").2)}Shuffle := falsereturn;------------------------------Button:buttonRow := SubStr(A_GuiControl, 1, 1), ButtonCol := SubStr(A_GuiControl, 3, 1)if Abs(buttonRow-Row) > 1 || Abs(ButtonCol-Col) > 1 || Abs(buttonRow-Row) = Abs(ButtonCol-Col)	returnMove(buttonRow-Row, ButtonCol-Col)return;------------------------------#IfWinActive, 15 Puzzle;------------------------------Down::Move(-1, 0)return;------------------------------Up::Move(1, 0)return;------------------------------Right::Move(0, -1)return;------------------------------Left::Move(0, 1)return;------------------------------#IfWinActive;------------------------------Move(deltaRow, deltaCol){	global	if (Row+deltaRow=0) || (Row+deltaRow=5) || (Col+deltaCol=0) || (Col+deltaCol=5)		return	GuiControl, Hide, % Row+deltaRow "_" Col+deltaCol	GuiControl, Show, % Row "_" Col	GuiControl,, %Row%_%Col%, % Grid[Row+deltaRow, Col+deltaCol]	Grid[Row, Col] := Grid[Row+deltaRow, Col+deltaCol]	Grid[Row+=deltaRow, Col+=deltaCol] := 16	if Shuffle		return	gridCont := ""	for m, obj in grid		for n, val in obj			gridCont .= val ","	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}`

## BASIC

### Commodore BASIC

`10 REM 15-PUZZLE GAME20 REM COMMODORE BASIC 2.030 REM ********************************40 GOSUB 400 : REM INTRO AND LEVEL50 GOSUB 510 : REM SETUP BOARD60 GOSUB 210 : REM PRINT PUZZLE70 PRINT "TO MOVE A PIECE, ENTER ITS NUMBER:"80 INPUT X90 GOSUB 760 : REM CHECK IF MOVE IS VALID100 IF MV=0 THEN PRINT "WRONG MOVE" : GOSUB 1130 : GOTO 60110 D(Z)=X : D(Y)=0120 GOSUB 210 : REM PRINT PUZZLE130 GOSUB 1030: REM CHECK IF PUZZLE COMPLETE140 IF PC THEN 160150 GOTO 70160 PRINT"YOU WON!"170 END180 REM190 REM *******************************200 REM PRINT/DRAW THE PUZZLE210 FOR P=1 TO 16220   IF D(P)=0 THEN D\$(P)="     " : GOTO 260230   S\$=STR\$(D(P))240   N=LEN(S\$)250   D\$(P) = LEFT\$("   ",3-N)+S\$+"  "260 NEXT270 PRINT "+-----+-----+-----+-----+"280 PRINT "!"D\$(1)"!"D\$(2)"!"D\$(3)"!"D\$(4)"!"290 PRINT "+-----+-----+-----+-----+"300 PRINT "!"D\$(5)"!"D\$(6)"!"D\$(7)"!"D\$(8)"!"310 PRINT "+-----+-----+-----+-----+"320 PRINT "!"D\$(9)"!"D\$(10)"!"D\$(11)"!"D\$(12)"!"330 PRINT "+-----+-----+-----+-----+"340 PRINT "!"D\$(13)"!"D\$(14)"!"D\$(15)"!"D\$(16)"!"350 PRINT "+-----+-----+-----+-----+"360 RETURN370 REM380 REM *******************************390 REM INTRO AND LEVEL OF DIFFICULTY400 PRINT CHR\$(147)410 DIM SH(3) : SH(1)=10 : SH(2)=50 : SH(3)=100420 PRINT "15 PUZZLE GAME FOR COMMODORE BASIC 2.0" : PRINT : PRINT430 PRINT "PLEASE ENTER LEVEL OF DIFFICULTY,"440 PRINT "1(EASY), 2(MEDIUM) OR 3(HARD):";450 INPUT V460 IF V<1 OR V>3 THEN 440470 RETURN480 REM490 REM *******************************500 REM BUILD THE BOARD510 DIM D(16) : DIM D\$(16) : REM BOARD PIECES520 REM SET PIECES IN CORRECT ORDER FIRST530 FOR P=1 TO 15540   D(P) = P550 NEXT560 D(16) = 0 : REM 0 = EMPTY PIECE/SLOT570 Z=16      : REM Z = EMPTY POSITION580 PRINT: PRINT "SHUFFLING PIECES";590 FOR N=1 TO SH(V)600   PRINT".";610   X = INT(RND(0)*4)+1620   IF X=1 THEN R=Z-4630   IF X=2 THEN R=Z+4640   IF (X=3) AND (INT((Z-1)/4)<>(Z-1)/4) THEN R=Z-1650   IF (X=4) AND (INT(Z/4)<>Z/4) THEN R=Z+1660   IF R<1 OR R>16 THEN 610670   D(Z)=D(R)680   Z=R690   D(Z)=0700 NEXT710 PRINT CHR\$(147)720 RETURN730 REM740 REM *******************************750 REM CHECK IF MOVE IS VALID760 MV = 0770 IF X<1 OR X>15 THEN RETURN780 REM FIND POSITION OF PIECE X AND OF EMPTY PIECE790 AX=X800 GOSUB 940 : REM FIND POSITION OF PIECE AX810 Y=P820 AX=0830 GOSUB 940 : REM FIND POSITION OF PIECE AX840 Z=P850 REM CHECK IF EMPTY PIECE IS ABOVE, BELOW, LEFT OR RIGHT TO PIECE X860 IF Y-4=Z THEN MV=1 : RETURN870 IF Y+4=Z THEN MV=1 : RETURN880 IF (Y-1=Z) AND (INT(Z/4)<>Z/4) THEN MV=1 : RETURN890 IF (Y+1=Z) AND (INT(Y/4)<>Y/4) THEN MV=1 : RETURN900 RETURN910 REM920 REM *******************************930 REM FIND POSITION OF PIECE AX940 P=1950 IF D(P)=AX THEN 990960   P=P+1970   IF P>16 THEN PRINT "UH OH!" : STOP980 GOTO 950990 RETURN1000 REM1010 REM *******************************1020 REM CHECK IF PUZZLE IS COMPLETE / GAME OVER1030 PC = 01040 P=11050 IF (P>=16) OR (D(P)<>P) THEN 10801060   P=P+11070 GOTO 10501080 IF P=16 THEN PC=11090 RETURN1100 REM1110 REM ******************************1120 REM A SMALL DELAY1130 FOR T=0 TO 4001140 NEXT1150 RETURN`

## BBC BASIC

Works with: ARM BBC BASIC
Works with: Brandy BASIC version Matrix Brandy
`      IF INKEY(-256)=77 OR (INKEY(-256) AND &F0)=&A0 THEN MODE 1: COLOUR 0: COLOUR 143: *FX4,1       SIZE=4 : DIFFICULTY=3       MAX=SIZE * SIZE - 1      DIM Board(MAX)      FOR I%=1 TO MAX : Board(I% - 1)=I% : NEXT      Gap=MAX      WHILE N% < DIFFICULTY ^ 2 PROCSlide(RND(4)) : ENDWHILE : REM Shuffle      N%=0       @%=2 + LOG(MAX + 1)      PROCShowAndTest      WHILE NOT Solved        PRINT "Use arrow keys to move the gap around. Moves taken: ";N%        PROCSlide(GET - 135)        PROCShowAndTest      ENDWHILE      PRINT "Solved after ";N% LEFT\$(" moves", 6 + (N% = 1)) "."      END       DEF PROCSlide(dir%)      NewGap=Gap      CASE dir% OF        WHEN 1: IF Gap MOD SIZE > 0        NewGap=Gap - 1    : N%+=1 : REM Left        WHEN 2: IF Gap MOD SIZE < SIZE - 1 NewGap=Gap + 1    : N%+=1 : REM Right        WHEN 3: IF Gap < MAX - SIZE + 1    NewGap=Gap + SIZE : N%+=1 : REM Down        WHEN 4: IF Gap > SIZE - 1          NewGap=Gap - SIZE : N%+=1 : REM Up      ENDCASE      SWAP Board(Gap), Board(NewGap)      Gap=NewGap      ENDPROC       DEF PROCShowAndTest      CLS      Solved=TRUE      FOR I%=0 TO MAX        COLOUR 12 : COLOUR 135        IF I% = Gap COLOUR 1 : COLOUR 129        IF I% MOD SIZE = SIZE - 1 PRINT Board(I%) ELSE PRINT Board(I%),;        IF Solved IF I% < MAX - 1 IF Board(I%) > Board(I% + 1) OR I% = Gap Solved=FALSE      NEXT      COLOUR 0 : COLOUR 143      PRINT      ENDPROC`

## 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.

`/* RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC, § 22 */#define _CRT_SECURE_NO_WARNINGS#include <stdio.h>#include <stdlib.h>#include <time.h>#define N 4#define M 4enum Move{UP,DOWN,LEFT,RIGHT};int hR;int hC;int cc[N][M];const int nS=100;intupdate(enum Move m){const int dx[]={0,0,-1,1};const int dy[]={-1,1,0,0};int i=hR+dy[m];int j=hC+dx[m];if(i>= 0&&i<N&&j>=0&&j<M){cc[hR][hC]=cc[i][j];cc[i][j]=0;hR=i;hC=j;return 1;}return 0;}void setup(void){int i,j,k;for(i=0;i<N;i++)for(j=0;j<M;j++)cc[i][j]=i*M+j+1;cc[N-1][M-1]=0;hR=N-1;hC=M-1;k=0;while(k<nS)k+=update((enum Move)(rand()%4));}int isEnd(void){int i,j; int k=1;for(i=0;i<N;i++)for(j=0;j<M;j++)if((k<N*M)&&(cc[i][j]!=k++))return 0;return 1;}void show(){int i,j;putchar('\n');for(i=0;i<N;i++)for(j=0;j<M;j++){if(cc[i][j])printf(j!=M-1?" %2d ":" %2d \n",cc[i][j]);else printf(j!=M-1?" %2s ":" %2s \n", "");}putchar('\n');}void disp(char* s){printf("\n%s\n", s);}enum Move get(void){int c;for(;;){printf("%s","enter u/d/l/r : ");c=getchar();while(getchar()!='\n');switch(c){case 27:exit(0);case'd':return UP;case'u':return DOWN;case'r':return LEFT;case'l':returnRIGHT;}}}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

`/* * RosettaCode: Fifteen puzle game, C89, plain vanillia TTY, MVC */ #define _CRT_SECURE_NO_WARNINGS /* unlocks printf etc. in MSVC */#include <stdio.h>#include <stdlib.h>#include <time.h> enum Move { MOVE_UP = 0, MOVE_DOWN = 1, MOVE_LEFT = 2, MOVE_RIGHT = 3 }; /* ***************************************************************************** * Model */ #define NROWS     4#define NCOLLUMNS 4int holeRow;       int holeCollumn;   int cells[NROWS][NCOLLUMNS];const int nShuffles = 100; int Game_update(enum Move move){    const int dx[] = {  0,  0, -1, +1 };    const int dy[] = { -1, +1,  0,  0 };    int i = holeRow     + dy[move];    int j = holeCollumn + dx[move];        if ( i >= 0 && i < NROWS && j >= 0 && j < NCOLLUMNS ){        cells[holeRow][holeCollumn] = cells[i][j];        cells[i][j] = 0; holeRow = i; holeCollumn = j;        return 1;    }    return 0;} void Game_setup(void){    int i,j,k;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ )            cells[i][j] = i * NCOLLUMNS + j + 1;    cells[NROWS-1][NCOLLUMNS-1] = 0;    holeRow = NROWS - 1;    holeCollumn = NCOLLUMNS - 1;    k = 0;    while ( k < nShuffles )        k += Game_update((enum Move)(rand() % 4));} int Game_isFinished(void){    int i,j; int k = 1;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ )             if ( (k < NROWS*NCOLLUMNS) && (cells[i][j] != k++ ) )                return 0;    return 1;        }  /* ***************************************************************************** * View  */ void View_showBoard(){    int i,j;    putchar('\n');    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ ){            if ( cells[i][j] )                printf(j != NCOLLUMNS-1 ? " %2d " : " %2d \n", cells[i][j]);            else                printf(j != NCOLLUMNS-1 ? " %2s " : " %2s \n", "");        }    putchar('\n');} void View_displayMessage(char* text){    printf("\n%s\n", text);}  /* ***************************************************************************** * Controller */ enum Move Controller_getMove(void){    int c;    for(;;){        printf("%s", "enter u/d/l/r : ");        c = getchar();        while( getchar() != '\n' )            ;        switch ( c ){            case 27: exit(EXIT_SUCCESS);            case 'd' : return MOVE_UP;               case 'u' : return MOVE_DOWN;            case 'r' : return MOVE_LEFT;            case 'l' : return MOVE_RIGHT;        }    }} void Controller_pause(void){    getchar();} int main(void){     srand((unsigned)time(NULL));     do Game_setup(); while ( Game_isFinished() );     View_showBoard();    while( !Game_isFinished() ){         Game_update( Controller_getMove() );         View_showBoard();     }     View_displayMessage("You win");    Controller_pause();     return EXIT_SUCCESS;}  `
Output:
```  9   1   4   7
6   5   3   2
13  10       8
14  15  11  12

enter u/d/l/r : u

9   1   4   7
6   5   3   2
13  10  11   8
14  15      12

enter u/d/l/r : l

9   1   4   7
6   5   3   2
13  10  11   8
14  15  12

enter u/d/l/r : d

9   1   4   7
6   5   3   2
13  10  11
14  15  12   8

enter u/d/l/r :```

### C89, long version, TTY/Winapi/ncurses modes

`/** * RosettaCode: Fifteen puzle game, C89, MS Windows Console API, MVC * * @version 0.2 (added TTY and ncurses modes) */ #define UNDEFINED_WIN32API_CONSOLE#define UNDEFINED_NCURSES_CONSOLE#if !defined (TTY_CONSOLE) && !defined(WIN32API_CONSOLE) && !defined(NCURSES_CONSOLE)#define TTY_CONSOLE#endif #define _CRT_SECURE_NO_WARNINGS    /* enable printf etc. */#define _CRT_NONSTDC_NO_DEPRECATE  /* POSIX functions enabled */ #include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#if defined(NCURSES_CONSOLE)#include "curses.h"  /* see http://pdcurses.sourceforge.net/ */#elif defined(WIN32API_CONSOLE)#define NOGDI                   /* we don't need GDI */#define WIN32_LEAN_AND_MEAN     /* we don't need OLE etc. */#include <windows.h>            /* MS Windows stuff */#include <conio.h>              /* kbhit() and getch() */#endif enum Move { MOVE_UP = 0, MOVE_DOWN = 1, MOVE_LEFT = 2, MOVE_RIGHT = 3 }; /* ***************************************************************************** * Model */ #define NROWS     4#define NCOLLUMNS 4int holeRow;       int holeCollumn;   int cells[NROWS][NCOLLUMNS];const int nShuffles = 100; int Game_update(enum Move move){    const int dx[] = {  0,  0, -1, +1 };    const int dy[] = { -1, +1,  0,  0 };    int i = holeRow     + dy[move];    int j = holeCollumn + dx[move];        if ( i >= 0 && i < NROWS && j >= 0 && j < NCOLLUMNS ){        cells[holeRow][holeCollumn] = cells[i][j];        cells[i][j] = 0; holeRow = i; holeCollumn = j;        return 1;    }    return 0;} void Game_setup(void){    int i,j,k;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ )            cells[i][j] = i * NCOLLUMNS + j + 1;    cells[NROWS-1][NCOLLUMNS-1] = 0;    holeRow = NROWS - 1;    holeCollumn = NCOLLUMNS - 1;    k = 0;    while ( k < nShuffles )        k += Game_update((enum Move)(rand() % 4));} int Game_isFinished(void){    int i,j; int k = 1;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ )             if ( (k < NROWS*NCOLLUMNS) && (cells[i][j] != k++ ) )                return 0;    return 1;        }  /* ***************************************************************************** * View  */ int fieldWidth;#ifdef WIN32API_CONSOLEHANDLE hConsole;CONSOLE_SCREEN_BUFFER_INFO csbi; #endif void View_setup_base(void){    int i;    fieldWidth = 0;    for ( i = NROWS * NCOLLUMNS - 1; i > 0; i /= 10 )        fieldWidth++;} #if defined(TTY_CONSOLE) void View_setup(void) {    View_setup_base();} void View_showBoard(){    int i,j;    putchar('\n');    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ ){            if ( cells[i][j] )                printf(j != NCOLLUMNS-1 ? " %*d " : " %*d \n", fieldWidth, cells[i][j]);            else                printf(j != NCOLLUMNS-1 ? " %*s " : " %*s \n", fieldWidth, "");        }    putchar('\n');} void View_displayMessage(char* text){    printf("\n%s\n", text);} #elif defined(NCURSES_CONSOLE) void View_setup(void) {    View_setup_base();    initscr();            clear();} void View_showBoard(){    int i,j;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ ){            int x = (fieldWidth+1)*j;            int y = 2*i;            if ( cells[i][j] ){                attron(A_REVERSE);                mvprintw(y,x,"%*d", fieldWidth, cells[i][j]);            }else{                attroff(A_REVERSE);                mvprintw(y,x,"%*s", fieldWidth, " ");            }        }    attrset(A_NORMAL);} void View_displayMessage(char* text){    mvprintw(2*NROWS,0, "%s", text);} #elif defined(WIN32API_CONSOLE) void View_setup(void) {    const COORD coordHome = { 0, 0 };     CONSOLE_CURSOR_INFO cci;    DWORD size, nWritten;    View_setup_base();    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);    cci.bVisible = FALSE;     cci.dwSize = 1;    SetConsoleCursorInfo(hConsole,&cci);        GetConsoleScreenBufferInfo(hConsole,&(csbi));    size = csbi.dwSize.X*csbi.dwSize.Y;    FillConsoleOutputCharacter(hConsole,' ',size,coordHome,&nWritten);    FillConsoleOutputAttribute(hConsole,csbi.wAttributes,size,coordHome,&nWritten); } void View_showBoard(){    int i,j;    char labelString[32];    WORD attributes;    DWORD nWritten;    for ( i = 0; i < NROWS; i++ )        for ( j = 0; j < NCOLLUMNS; j++ ){            COORD coord = { ((SHORT)fieldWidth+1)*j, coord.Y = 2*i };            if ( cells[i][j] ){                sprintf(labelString,"%*d", fieldWidth, cells[i][j]);                                attributes = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;            }else{                sprintf(labelString,"%*s", fieldWidth, " ");                attributes = csbi.wAttributes;            }            WriteConsoleOutputCharacter(hConsole,labelString,fieldWidth,coord,&nWritten);            FillConsoleOutputAttribute (hConsole,attributes,fieldWidth,coord,&nWritten);        }} void View_displayMessage(char* text){    DWORD nWritten;    COORD coord = { 0, 2 * NROWS };    WriteConsoleOutputCharacter(hConsole,text,strlen(text),coord,&nWritten);} #endif  /* ***************************************************************************** * Controller */ #if defined(TTY_CONSOLE) void Controller_setup(void){} enum Move Controller_getMove(void){    int c;    for(;;){        printf("%s", "enter u/d/l/r : ");        c = getchar();        while( getchar() != '\n' )            ;        switch ( c ){            case 27: exit(EXIT_SUCCESS);            case 'd' : return MOVE_UP;               case 'u' : return MOVE_DOWN;            case 'r' : return MOVE_LEFT;            case 'l' : return MOVE_RIGHT;        }    }} void Controller_pause(void){    getchar();} #elif defined(NCURSES_CONSOLE) void Controller_setup(void){    noecho();    cbreak();    curs_set(0);    keypad(stdscr,TRUE);} enum Move Controller_getMove(void){    for(;;){        switch ( wgetch(stdscr) ){            case  27: exit(EXIT_SUCCESS);            case KEY_DOWN  : return MOVE_UP;               case KEY_UP    : return MOVE_DOWN;            case KEY_RIGHT : return MOVE_LEFT;            case KEY_LEFT  : return MOVE_RIGHT;            case ERR: /* NOP */;        }    }} void Controller_pause(void){    while ( wgetch(stdscr) == ERR )        ;}  #elif defined(WIN32API_CONSOLE) void Controller_setup(void){} enum Move Controller_getMove(void){    for(;;){        switch ( getch() ){            case  27: exit(EXIT_SUCCESS);            case   0:            case 224: switch ( getch() ){                case 80 : return MOVE_UP;                   case 72 : return MOVE_DOWN;                case 77 : return MOVE_LEFT;                case 75 : return MOVE_RIGHT;            }        }    }} void Controller_pause(void){    while(  kbhit() ) getch();    while( !kbhit() )   ;    while(  kbhit() ) getch();} #endif  /* ***************************************************************************** * Main function: create model, view and controller. Run main loop. */int main(void) {     srand((unsigned)time(NULL));     do Game_setup(); while ( Game_isFinished() );    View_setup();     Controller_setup();     View_showBoard();    while( !Game_isFinished() ){         Game_update( Controller_getMove() );         View_showBoard();     }     View_displayMessage("You win");    Controller_pause();     return EXIT_SUCCESS;} `

## C#

Works with: C sharp version 6
`using System;using System.Drawing;using System.Linq;using System.Windows.Forms; public class FifteenPuzzle{    const int gridSize = 4; //Standard 15 puzzle is 4x4    const bool evenSized = gridSize % 2 == 0;    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 void Main(string[] args)    {        FifteenPuzzle p = new FifteenPuzzle();        Form f = p.BuildForm();        Application.Run(f);    }     public FifteenPuzzle()    {        for (int i = 0; i < blockCount; i++) {            grid[i] = i;            positionOf[i] = i;        }    }     Form BuildForm()    {        Button startButton = new Button {            Font = new Font("Arial", 9.75F, FontStyle.Regular, GraphicsUnit.Point, ((byte)(0))),            Size = new Size(86, 23),            Location = new Point(formEdge,                (buttonSize + buttonMargin * 2) * gridSize + buttonMargin + formEdge),            Text = "New Game",            UseVisualStyleBackColor = true        };        startButton.Click += (sender, e) => Shuffle();         int size = buttonSize * gridSize + buttonMargin * gridSize * 2 + formEdge * 2;        Form form = new Form {            Text = "Fifteen",            ClientSize = new Size(width: size, height: size + buttonMargin * 2 + startButton.Height)        };        form.SuspendLayout();        for (int index = 0; index < blockCount; index++) {            Button button = new Button {                Font = buttonFont,                Size = new Size(buttonSize, buttonSize),                //Margin = new Padding(buttonMargin),                Text = (index + 1).ToString(),                UseVisualStyleBackColor = true            };            SetLocation(button, index);            form.Controls.Add(button);            buttons[index] = button;            int i = index;            button.Click += (sender, e) => ButtonClick(i);        }        form.Controls.Add(startButton);        form.ResumeLayout();        return form;    }     void ButtonClick(int i)    {        if (buttons[last].Visible) return;        int target = positionOf[i];        if (positionOf[i] / gridSize == positionOf[last] / gridSize) {            while (positionOf[last] < target) {                Swap(last, grid[positionOf[last] + 1]);                moves++;            }            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++;            }        }        if (Solved()) {            TimeSpan elapsed = DateTime.Now - start;            elapsed = TimeSpan.FromSeconds(Math.Round(elapsed.TotalSeconds, 0));            buttons[last].Visible = true;            MessageBox.Show(\$"Solved in {moves} moves. Time: {elapsed}");        }    }     bool Solved() => Enumerable.Range(0, blockCount - 1).All(i => positionOf[i] == i);     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);    }     void Shuffle()    {        for (int i = 0; i < blockCount; i++) {            int r = rnd.Next(i, blockCount);            int g = grid[r];            grid[r] = grid[i];            grid[i] = g;        }        for (int i = 0; i < blockCount; i++) {            positionOf[grid[i]] = i;            SetLocation(buttons[grid[i]], i);        }        if (!Solvable()) Swap(0, 1); //Swap any 2 blocks         buttons[last].Visible = false;        moves = 0;        start = DateTime.Now;    }     bool Solvable()    {        bool parity = true;        for (int i = 0; i < blockCount - 2; i++) {            for (int j = i + 1; j < blockCount - 1; j++) {                if (positionOf[j] < positionOf[i]) parity = !parity;            }        }        if (evenSized && positionOf[last] / gridSize % 2 == 0) parity = !parity;        return parity;    }     void Swap(int a, int b)    {        Point location = buttons[a].Location;        buttons[a].Location = buttons[b].Location;        buttons[b].Location = location;         int p = positionOf[a];        positionOf[a] = positionOf[b];        positionOf[b] = p;         grid[positionOf[a]] = a;        grid[positionOf[b]] = b;    }}`

## C++

` #include <time.h>#include <stdlib.h>#include <vector>#include <string>#include <iostream>class p15 {public :    void play() {        bool p = true;        std::string a;        while( p ) {            createBrd();            while( !isDone() ) { drawBrd();getMove(); }            drawBrd();            std::cout << "\n\nCongratulations!\nPlay again (Y/N)?";            std::cin >> a; if( a != "Y" && a != "y" ) break;        }    }private:    void createBrd() {        int i = 1; std::vector<int> v;        for( ; i < 16; i++ ) { brd[i - 1] = i; }        brd[15] = 0; x = y = 3;        for( i = 0; i < 1000; i++ ) {            getCandidates( v );            move( v[rand() % v.size()] );            v.clear();        }    }    void move( int d ) {        int t = x + y * 4;        switch( d ) {            case 1: y--; break;            case 2: x++; break;            case 4: y++; break;            case 8: x--;        }        brd[t] = brd[x + y * 4];        brd[x + y * 4] = 0;    }    void getCandidates( std::vector<int>& v ) {        if( x < 3 ) v.push_back( 2 ); if( x > 0 ) v.push_back( 8 );        if( y < 3 ) v.push_back( 4 ); if( y > 0 ) v.push_back( 1 );    }    void drawBrd() {        int r; std::cout << "\n\n";        for( int y = 0; y < 4; y++ ) {            std::cout << "+----+----+----+----+\n";            for( int x = 0; x < 4; x++ ) {                r = brd[x + y * 4];                std::cout << "| ";                if( r < 10 ) std::cout << " ";                if( !r ) std::cout << "  ";                else std::cout << r << " ";            }            std::cout << "|\n";        }        std::cout << "+----+----+----+----+\n";    }    void getMove() {        std::vector<int> v; getCandidates( v );        std::vector<int> p; getTiles( p, v ); unsigned int i;        while( true ) {            std::cout << "\nPossible moves: ";            for( i = 0; i < p.size(); i++ ) std::cout << p[i] << " ";            int z; std::cin >> z;            for( i = 0; i < p.size(); i++ )                if( z == p[i] ) { move( v[i] ); return; }        }    }    void getTiles( std::vector<int>& p, std::vector<int>& v ) {        for( unsigned int t = 0; t < v.size(); t++ ) {            int xx = x, yy = y;            switch( v[t] ) {                case 1: yy--; break;                case 2: xx++; break;                case 4: yy++; break;                case 8: xx--;            }            p.push_back( brd[xx + yy * 4] );        }    }    bool isDone() {        for( int i = 0; i < 15; i++ ) {            if( brd[i] != i + 1 ) return false;        }        return true;    }    int brd[16], x, y;};int main( int argc, char* argv[] ) {    srand( ( unsigned )time( 0 ) );    p15 p; p.play(); return 0;} `
```+----+----+----+----+
| 11 |  5 | 12 |  3 |
+----+----+----+----+
| 10 |  7 |  6 |  4 |
+----+----+----+----+
| 13 |    |  2 |  1 |
+----+----+----+----+
| 15 | 14 |  8 |  9 |
+----+----+----+----+

Possible moves: 2 13 14 7
```

## COBOL

Tested with GnuCOBOL

`         >>SOURCE FORMAT FREE*> This code is dedicated to the public domain*> This is GNUCOBOL 2.0identification division.program-id. fifteen.environment division.configuration section.repository. function all intrinsic.data division.working-storage section. 01  r pic 9.01  r-empty pic 9.01  r-to pic 9.01  r-from pic 9. 01  c pic 9.01  c-empty pic 9.01  c-to pic 9.01  c-from pic 9. 01  display-table.    03  display-row occurs 4.        05  display-cell occurs 4 pic 99. 01  tile-number pic 99.01  tile-flags pic x(16). 01  display-move value spaces.    03  tile-id pic 99. 01  row-separator pic x(21) value all '.'.01  column-separator pic x(3) value ' . '. 01  inversions pic 99.01  current-tile pic 99. 01  winning-display pic x(32) value        '01020304'    &   '05060708'    &   '09101112'    &   '13141500'. procedure division.start-fifteen.    display 'start fifteen puzzle'    display '    enter a two-digit tile number and press <enter> to move'    display '    press <enter> only to exit'     *> tables with an odd number of inversions are not solvable    perform initialize-table with test after until inversions = 0    perform show-table    accept display-move    perform until display-move = spaces        perform move-tile        perform show-table        move spaces to display-move        accept display-move    end-perform    stop run    .initialize-table.    compute tile-number = random(seconds-past-midnight) *> seed only    move spaces to tile-flags    move 0 to current-tile inversions    perform varying r from 1 by 1 until r > 4    after c from 1 by 1 until c > 4        perform with test after        until tile-flags(tile-number + 1:1) = space            compute tile-number = random() * 100            compute tile-number = mod(tile-number, 16)        end-perform        move 'x' to tile-flags(tile-number + 1:1)        if tile-number > 0 and < current-tile            add 1 to inversions        end-if        move tile-number to display-cell(r,c) current-tile    end-perform    compute inversions = mod(inversions,2)    .show-table.    if display-table = winning-display        display 'winning'    end-if    display space row-separator    perform varying r from 1 by 1 until r > 4        perform varying c from 1 by 1 until c > 4            display column-separator with no advancing            if display-cell(r,c) = 00                display '  ' with no advancing                move r to r-empty                move c to c-empty            else                display display-cell(r,c) with no advancing            end-if        end-perform        display column-separator    end-perform    display space row-separator    .move-tile.    if not (tile-id numeric and tile-id >= 01 and <= 15)        display 'invalid tile number'        exit paragraph    end-if     *> find the entered tile-id row and column (r,c)    perform varying r from 1 by 1 until r > 4    after c from 1 by 1 until c > 4        if display-cell(r,c) = tile-id            exit perform        end-if    end-perform     *> show-table filled (r-empty,c-empty)    evaluate true    when r = r-empty        if c-empty < c            *> shift left            perform varying c-to from c-empty by 1 until c-to > c                compute c-from = c-to + 1                move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)            end-perform        else           *> shift right           perform varying c-to from c-empty by -1 until c-to < c               compute c-from = c-to - 1               move display-cell(r-empty,c-from) to display-cell(r-empty,c-to)           end-perform       end-if       move 00 to display-cell(r,c)    when c = c-empty        if r-empty < r            *>shift up            perform varying r-to from r-empty by 1 until r-to > r                compute r-from = r-to + 1                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)            end-perform        else            *> shift down            perform varying r-to from r-empty by -1 until r-to < r                compute r-from = r-to - 1                move display-cell(r-from,c-empty) to display-cell(r-to,c-empty)            end-perform        end-if        move 00 to display-cell(r,c)    when other         display 'invalid move'    end-evaluate    .end program fifteen.`
Output:
```prompt\$ cobc -xj fifteen.cbl
start fifteen puzzle
enter a two-digit tile number and press <enter> to move
press <enter> only to exit
.....................
. 05 . 14 . 08 . 12 .
. 01 . 10 . 03 . 09 .
. 02 . 15 . 13 . 11 .
. 06 .    . 07 . 04 .
.....................
```

## Common Lisp

Credit to this post for help with the inversions-counting function: [1]

`|15|::main`
.
`(defpackage :15  (:use :common-lisp))(in-package :15) (defvar +side+ 4)(defvar +max+ (1- (* +side+ +side+))) ; 15 (defun make-board ()  (make-array (list +side+ +side+)              :initial-contents              (loop :for i :below +side+ :collecting                 (loop :for j :below +side+ :collecting                    (mod (1+ (+ j (* i +side+))) (1+ +max+))))))(defvar *board* (make-board)) (defun shuffle-board (board)  (loop for i from (array-total-size board) downto 2     do (rotatef (row-major-aref board (random i))                 (row-major-aref board (1- i))))  board) (defun pb (stream object &rest args)  (declare (ignorable args))  (loop for i below (car (array-dimensions object)) do       (loop for j below (cadr (array-dimensions object)) do            (let ((cell (aref object i j)))              (format stream "(~[  ~:;~:*~2d~])" cell)))       (format stream "~%"))) (defun sortedp (board)  (declare (ignorable board))  (loop for i upto +max+     when (eq (row-major-aref board i) (mod (1+ i) 16)) do       (return-from sortedp nil))  t) (defun inversions (lst)  (if (or (null lst) (null (cdr lst)))      0      (let* ((half (ceiling (/ (length lst) 2)))             (left-list (subseq lst 0 half))             (right-list (subseq lst half)))        (+ (loop for a in left-list              summing (loop for b in right-list                         counting (not (< a b))))           (inversions left-list)           (inversions right-list))))) (defun solvablep (board)  (let ((inv (inversions (loop for i upto +max+ collecting                              (row-major-aref board i))))        (row (- +side+ (first (board-position board 0)))))    (or (and (oddp +side+)             (evenp inv))        (and (evenp +side+)             (evenp row)             (oddp inv))        (and (evenp +side+)             (oddp row)             (evenp inv))))) (defun board-position (board dig)  (loop for i below (car (array-dimensions board)) do       (loop for j below (cadr (array-dimensions board))          when (eq dig (aref board i j)) do          (return-from board-position (list i j))))) (defun in-bounds (y x)  (and (< -1 y +side+)       (< -1 x +side+))) (defun get-adjacents (board pos)  (let ((adjacents ()) (y (first pos)) (x (second pos)))    (if (in-bounds y (1+ x))        (push (aref board y (1+ x)) adjacents))    (if (in-bounds (1+ y) x)        (push (aref board (1+ y) x) adjacents))    (if (in-bounds y (1- x))        (push (aref board y (1- x)) adjacents))    (if (in-bounds (1- y) x)        (push (aref board (1- y) x) adjacents))    adjacents)) (defun main (&rest argv)  (declare (ignorable argv))  (setf *random-state* (make-random-state t))  (loop until (solvablep *board*) do       (shuffle-board *board*))  (loop until (sortedp *board*) do       (format t "~/15:pb/~%" *board*)       (format t "Which number do you want to swap the blank with?~%> ")       (let* ((in (read))              (zpos (board-position *board* 0))              (pos (board-position *board* in))              (adj (get-adjacents *board* zpos)))         (if (find in adj)             (rotatef (aref *board* (first pos) (second pos))                      (aref *board* (first zpos) (second zpos))))))  (format t "You win!~%"))`

## EasyLang

`set_background 432set_textsize 13len f[] 16func draw . .  clear_screen  for i range 16    h = f[i]    if h < 16      x = i mod 4 * 24 + 3      y = i div 4 * 24 + 3      set_color 210      move_pen x y      draw_rect 22 22      move_pen x + 4 y + 5      if h < 10        move_pen x + 6 y + 5      .      set_color 885      draw_text h    .  ..global done .func init . .  done = 0  for i range 16    f[i] = i + 1  .  # shuffle  for i = 14 downto 1    r = random (i + 1)    swap f[r] f[i]  .  # make it solvable  inv = 0  for i range 15    for j range i      if f[j] > f[i]        inv += 1      .    .  .  if inv mod 2 <> 0    swap f[0] f[1]  .  set_textsize 12  call draw.func move_tile . .  c = mouse_x div 25  r = mouse_y div 25  i = r * 4 + c  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 = 1  for i range 15    if f[i] > f[i + 1]      done = 0    .  .  if done = 1    clear_screen    move_pen 10 30    draw_text "Well done!"  ..on mouse_down  if done = 1    call init  else    call move_tile  ..call init`

## F#

` // 15 Puzzle Game. Nigel Galloway: August 9th., 2020let 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()let rec fN g Σ=function h::t->fN g (Σ+List.sumBy(fun n->if h>n then 1 else 0)t) t|_->(Σ-g/4)%2=1let rec fI g=match if System.Console.IsInputRedirected then char(System.Console.Read()) else System.Console.ReadKey(true).KeyChar with               n when Seq.contains n g->printf "%c" n; (match n with 'l'-> -1|'r'->1|'d'->4|_-> -4)|_->System.Console.Beep(); fI g let rec fG n Σ=function 0->(List.findIndex((=)0)Σ,Σ)|g->let i=List.item(rnd.Next(g)) n in fG(List.except [i] n)(i::Σ)(g-1)let rec fE()=let n,g=fG [0..15] [] 16 in if fN n 0 (List.except [0] g) then (n,Array.ofList g) else fE()let rec fL(n,g) Σ=let fa=printfn "";Array.chunkBySize 4 g|>Array.iter(fun n->Array.iter(printf "%3d")n;printfn "")                  match g=RA with true->printfn "Solved in %d moves" Σ; fa; 0                                 |_->let vM=match n/4,n%4 with (0,0)->"rd"|(0,3)->"ld"|(0,_)->"lrd"|(3,0)->"ru"|(3,3)->"lu"|(3,_)->"lru"|(_,0)->"rud"|(_,3)->"lud"|_->"lrud"                                     fa; printf "Move Number: %2d; Manhatten Distance: %2d; Choose move from %4s: " Σ                                         ([0..15]|>List.sumBy(fun n->match g.[n] with 0->0 |i->(abs(n/4-Nr.[i]))+(abs(n%4-Nc.[i])))) vM                                     let v=fI vM in g.[n]<-g.[n+v];g.[n+v]<-0;fL(n+v,g)(Σ+1)[<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  `

3 uses:

```15 game with no parameters will generate a random game which may be solved.
A solution in the form suggested in 15_puzzle_solver may be piped into 15game. "dddrurdruuulllddrulddrrruuullddruulldddrrurulldrruulldlddrurullddrrruullulddrdrr" | .\15game 0x0c9dfbae37254861 produces the output here: 15_puzzle_solver/extra_credit/solution.
Optionally a start position may be supplied. 15game 0x123450689a7bdefc may be solved:
```
Output:
```  1  2  3  4
5  0  6  8
9 10  7 11
13 14 15 12
Move Number:  0; Manhatten Distance:  4; Choose move from lrud: r
1  2  3  4
5  6  0  8
9 10  7 11
13 14 15 12
Move Number:  1; Manhatten Distance:  3; Choose move from lrud: d
1  2  3  4
5  6  7  8
9 10  0 11
13 14 15 12
Move Number:  2; Manhatten Distance:  2; Choose move from lrud: r
1  2  3  4
5  6  7  8
9 10 11  0
13 14 15 12
Move Number:  3; Manhatten Distance:  1; Choose move from  lud: d
1  2  3  4
5  6  7  8
9 10 11 12
13 14 15  0
Solved in 4 moves
```

## Factor

`USING: accessors combinators combinators.extrascombinators.short-circuit grouping io kernel literals mathmath.matrices math.order math.parser math.vectors prettyprint qwrandom sequences sequences.extras ;IN: rosetta-code.15-puzzle-game << TUPLE: board matrix zero ; : <board> ( -- board )    16 <iota> 1 rotate 4 group { 3 3 } board boa ; >> CONSTANT: winning \$[ <board> matrix>> ] : input>dir ( str -- pair )    {        { "u" [ { 1 0 } ] }        { "d" [ { -1 0 } ] }        { "l" [ { 0 1 } ] }        { "r" [ { 0 -1 } ] }    } case ; : get-index ( loc matrix -- elt ) [ first2 swap ] dip nth nth ; : mexchange ( loc1 loc2 matrix -- )    tuck [ [ [ get-index ] keepd ] [email protected] ] keep [ spin ] 2dip    [ set-index ] keep set-index ; : vclamp+ ( seq1 seq2 -- seq ) v+ { 0 0 } { 3 3 } vclamp ; : slide-piece ( board str -- )    over zero>> [ vclamp+ ] keep rot matrix>> mexchange ; : move-zero ( board str -- )    [ vclamp+ ] curry change-zero drop ; : move ( board str -- )    input>dir [ slide-piece ] [ move-zero ] 2bi ; : rand-move ( board -- ) qw{ u d l r } random move ; : shuffle-board ( board n -- board' ) [ dup rand-move ] times ; : .board ( board -- ) matrix>> simple-table. ; : get-input ( -- str )    "Your move? (u/d/l/r/q) " write flush readln dup    qw{ u d l r q } member? [ drop get-input ] unless ; : won? ( board -- ? ) matrix>> winning = ; DEFER: game: process-input ( board -- board' )    get-input dup "q" = [ drop ] [ game ] if ; : check-win ( board -- board' )    dup won? [ "You won!" print ] [ process-input ] if ; : game ( board str -- board' )    [ move ] keepd dup .board check-win ; : valid-difficulty? ( obj -- ? )    { [ fixnum? ] [ 3 100 between? ] } 1&& ; : choose-difficulty ( -- n )    "How many shuffles? (3-100) " write flush readln    string>number dup valid-difficulty?    [ drop choose-difficulty ] unless ; : main ( -- )    <board> choose-difficulty shuffle-board dup .board check-win    drop ; MAIN: main`
Output:
```How many shuffles? (3-100) 5
1  2  3  4
5  6  7  8
9  0  10 12
13 14 11 15
1  2  3  4
5  6  7  8
9  10 0  12
13 14 11 15
1  2  3  4
5  6  7  8
9  10 11 12
13 14 0  15
1  2  3  4
5  6  7  8
9  10 11 12
13 14 15 0
You won!
```

## Forth

Works with: gforth version 0.7.2

The code tested with gforth 0.7.2. It required a 64-bit system.

See 15_puzzle_solver#Forth for a solver based on the same code.

`#! /usr/bin/gforth cell 8 <> [if] s" 64-bit system required" exception throw [then] \ In the stack comments below,\ "h" stands for the hole position (0..15),\ "s" for a 64-bit integer representing a board state,\ "t" a tile value (0..15, 0 is the hole),\ "b" for a bit offset of a position within a state,\ "m" for a masked value (4 bits selected out of a 64-bit state),\ "w" for a weight of a current path,\ "d" for a direction constant (0..3) \ Utility: 3dup   2 pick 2 pick 2 pick ;: 4dup   2over 2over ;: shift   dup 0 > if lshift else negate rshift then ; hex 123456789abcdef0 decimal constant solution: row   2 rshift ;   : col   3 and ; : up-valid?    ( h -- f ) row 0 > ;: down-valid?  ( h -- f ) row 3 < ;: left-valid?  ( h -- f ) col 0 > ;: right-valid? ( h -- f ) col 3 < ; \ To iterate over all possible directions, put direction-related functions into arrays:: ith ( u addr -- w ) swap cells + @ ;create valid? ' up-valid? , ' left-valid? , ' right-valid? , ' down-valid? , does> ith execute ;create step -4 , -1 , 1 , 4 , does> ith ; \ Advance from a single state to another:: bits ( h -- b ) 15 swap - 4 * ;: tile ( s b -- t ) rshift 15 and ;: new-state ( s h d -- s' ) step dup >r + bits 2dup tile ( s b t ) swap lshift tuck - swap r> 4 * shift + ; : hole? ( s u -- f ) bits tile 0= ;: hole ( s -- h ) 16 0 do dup i hole? if drop i unloop exit then loop drop ; 0 constant up 1 constant left 2 constant right 3 constant down \ Print a board:: .hole   space space space ;: .tile ( u -- ) ?dup-0=-if .hole else dup 10 < if space then . then ;: .board ( s -- ) 4 0 do cr 4 0 do dup j 4 * i + bits tile .tile loop loop drop ;: .help   cr ." ijkl move, q quit" ; \ Pseudorandom number generator:create (rnd)   utime drop ,: rnd   (rnd) @ dup 13 lshift xor dup 17 rshift xor dup dup 5 lshift xor (rnd) ! ; : move ( s u -- s' ) >r dup hole r> new-state ;: ?move ( s u -- s' ) >r dup hole [email protected] valid? if r> move else rdrop then ;: shuffle ( s u -- s' ) 0 do rnd 3 and ?move loop ; : win   cr ." you won!" bye ;: turn ( s -- )	page dup .board .help	key case		[char] q of bye endof		[char] i of down ?move endof		[char] j of right ?move endof		[char] k of up ?move endof		[char] l of left ?move endof	endcase ; : play  begin dup solution <> while turn repeat win ; solution 1000 shuffle play `

## Fortran

The initial version had me so enamoured by the notion of consecutive cells for the solution having the number of their index as their value (as in CELL(0) = 0 (the blank square), CELL(1) = 1, ... CELL(15) = 15) and the prospect of the check for this being simple, that I failed to perceive that the nice big diagram of the board shown at the head of the article in fact clearly shows the solution state having the blank cell at the end, not the start. Once again it is demonstrated that what you see is ... influenced ... by what you would like to see. After that diversion, the cells shall now be numbered one to sixteen, not zero to fifteen, and so there is no need for the ability introduced by F90 whereby arrays can have a lower bound other than one.

The plan is to use parameters for the board size, which need not be square. As often with Fortran, messing with arrays is the key, though not without opportunities for confusion. Because Fortran stores arrays in column-major order, the arrays are accessed as BOARD(column,row) even though the arrangement is treated as rows down the page and columns across as is usual. By this means, consecutive elements in storage of array BOARD(c,r) are such that the same storage accessed via array BORED(i) thanks to `EQUIVALENCE(BOARD,BORED)` indexes them as consecutive elements, and so the test that the values are in consecutive order becomes delightfully simple, though alas there is no equivalent of the iota function of APL whereby the test could be `ALL(BORED(1:N - 1) .EQ. IOTA(N - 1))`

Column-major ordering also applies to array WAY, which lists the offsets needed to locate squares deemed adjacent to a given location, such as that of the blank square, located by LOCI = LOCZ + WAY(i). Adjacent LOCI are checked for being in range, and if so, added to the list in array LOCM with the moveable piece identified in array MOVE.

It transpires that the F90 compiler will not allow a PARAMETER statement to define values for items appearing in an EQUIVALENCE statement; so much for an attempt to do so in a systematic manner employing related names.

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
`LOCZ = MINLOC(BOARD)	!Find the zero. 0 = BOARD(LOCZ(1),LOCZ(2)) == BOARD(ZC,ZR)`
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.
`      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.        I = J			!One could define a MODULE containing a collection        J = T			!And thence a "generic" routine,      END SUBROUTINE SWAP	!But this will do for now.       SUBROUTINE SHOW(NR,NC,BOARD)	!The layout won't work for NC > 99...       INTEGER NR,NC		!Number of rows and columns.       INTEGER BOARD(NC,NR)	!The board is stored transposed!       INTEGER I		!A stepper.       COMMON/IODEV/ MSG	!I talk to the trees...        WRITE (MSG,1) (I,I = 1,NC)	!Prepare a heading.    1   FORMAT ("Row|",9("__",I1,:),90("_",I2,:))	!This should suffice.        DO I = 1,NR		!Chug down the rows.          WRITE (MSG,2) I,BOARD(1:NC,I)	!The columns of the row. Usage is BOARD(column,row).    2     FORMAT (I3,"|",99I3)	!Could use parameters, but enough.        END DO			!On to the next row.      END SUBROUTINE SHOW	!That was nice.       PROGRAM PUZZLE      INTEGER LOCN(2),NR,NC,N	!Describes the shape of the board.      INTEGER LOCZ(2),ZC,ZR	!Fingers the location of the "blank" square.      INTEGER LOCI(2),IC,IR	!Fingers a location.Can't EQUIVALENCE (LOCN(1),NC),(LOCN(2),NR)	!This usage and a PARAMETER statement is too scary.      EQUIVALENCE (LOCZ(1),ZC),(LOCZ(2),ZR)	!Annotate my (column,row) usage.      EQUIVALENCE (LOCI(1),IC),(LOCI(2),IR)	!Rather than the displayed (row,column) style.      PARAMETER (NR = 4, NC = 4, N = NR*NC)	!Determine the shape of the board.      INTEGER BOARD(NC,NR)		!Thus transpose furrytran's column-major usage. Beware!!!      INTEGER BORED(N)			!This allows for consecutive comparisons.      EQUIVALENCE (BOARD,BORED)		!Because the arrays are in the same place.      INTEGER WAYS			!Now define adjacency.      PARAMETER (WAYS = 4)		!Just orthogonal neghbours.      INTEGER WAY(2,WAYS)		!Now list the allowed adjacencies.      PARAMETER (WAY = (/1,0, 0,1, -1,0, 0,-1/))	!W(1,1), W(2,1), W(1,2), W(2,2), W(1,3), ...      INTEGER M,MOVE(WAYS),LOCM(2,WAYS)	!Move possibilities.      INTEGER SPACE			!Document the empty square's code number.      PARAMETER (SPACE = 0)		!Zero will do.      INTEGER I,IT,PARITY,TRY		!Odds and ends.      REAL VALUE			!Humph. Yet another interface to a "random" number generator.      COMMON/IODEV/ MSG,KBD	!Pass the word.       KBD = 5	!Standard input. (Keyboard -> Display screen)      MSG = 6	!Standard output. (Display screen)      WRITE (MSG,1) NR,NC	!Announce.    1 FORMAT ("To play 'slide-square' with ",I0," rows and ",     1 I0," columns.",/,"The game is to slide a square into the space",/     2 "(thus leaving a space behind) until you attain"/     3 "the nice orderly layout as follows:",/)Concoct a board layout.   10 FOR ALL (I = 1:N - 1) BORED(I) = I	!Prepare the board. Definitely unique values.      BORED(N) = SPACE	        !The empty square is not at the start! Oh well.      CALL SHOW(NR,NC,BOARD)	!Reveal the nice layout.   11 DO I = 1,N - 1		!Now shuffle the squares a bit.        CALL RANDOM(VALUE)		!0 <= VALUE < 1.        IT = VALUE*(N - 1) + 1		!1 <= IT < N. Don't round up!        IF (I.NE.IT) CALL SWAP(BORED(I),BORED(IT))	!Whee!      END DO			!On to the next victim, leaving the last cell alone.Calculate the parity, knowing the space is at the end. The target state has even parity, with zero inversions.      PARITY = 0	!There are two classes of arrangements, that can't mix.      DO I = 1,N - 2	!Skipping the blank cell still at BORED(N).        PARITY = PARITY + COUNT(BORED(I) > BORED(I + 1:N - 1))	!For each square,      END DO		!Count the inversions following.      IF (MOD(PARITY,2).NE.0) GO TO 11	!No transition can change the parity, so, try for another arrangement.Choose a new position for the space. Using approved moves will not change the parity.      CALL RANDOM(VALUE)		!0 <= VALUE < 1.      ZC = VALUE*(NC - 2) + 1		!1 <= ZC < NC: Choose a random column other than the last.      BOARD(ZC + 1:NC,NR) = BOARD(ZC:NC - 1,NR)	!Shift the end of the last row back one place.      BOARD(ZC,NR) = SPACE			!Put the space in the hole.      CALL RANDOM(VALUE)			!So the parity doesn't change.      ZR = VALUE*(NR - 2) + 1		!1 <= ZR < NR: Choose a random row, other than the last.      BOARD(ZC,ZR + 1:NR) = BOARD(ZC,ZR:NR - 1)	!Shift the end of column ZC up one.      BOARD(ZC,ZR) = SPACE			!Revive the space again.Cast forth the starting position.      WRITE (MSG,12)		!Announce the result.   12 FORMAT (/,"But, your board looks like this...")	!Alas. Almost certainly not in order.      CALL SHOW(NR,NC,BOARD)	!Just so.      TRY = 0		!No moves made yet. Consider possible moves.   20 TRY = TRY + 1	!Here we go again.      M = 0		!No moveable pieces are known.      DO I = 1,WAYS	!So scan the possible ways away from LOCZ.        LOCI = LOCZ + WAY(1:2,I)	!Finger an adjacent location, via the adjacency offsets in array WAY.        IF (ALL(LOCI > 0) .AND. ALL(LOCI <= (/NC,NR/))) THEN	!Within bounds?          M = M + 1			!Yes. We have a candidate.          MOVE(M) = BOARD(IC,IR)	!Record the piece's name.          LOCM(:,M) = LOCI		!And, remember where it is...        END IF			!So much for that location.      END DO		!Try another offset.   21 WRITE (MSG,22,ADVANCE="no") MOVE(1:M)	!Two-stage output.   22 FORMAT ("Moveable pieces: ",<WAYS>(I0:","))	!Since M is not necessarily WAYS, a trailing \$ may not be reached..      WRITE (MSG,23)		!Now for the question. Always at least two moveable squares.   23 FORMAT(". Choose one: ",\$)	!Continue the line, presuming screen and keyboard->screen.      READ (KBD,*) IT		!Now request the answer. Rather doggedly: blank lines won't do.      DO I = M,1,-1		!There are at least two possible moves.        IF (MOVE(I) .EQ. IT) EXIT	!Perhaps this piece was selected.      END DO			!The INDEX function is alas, only for CHARACTER variables. Grr.      IF (I .LE. 0) THEN	!I'm suspicious.        WRITE (MSG,*) "Huh? That is not a moveable piece!"	!Justified!        IF (IT.GT.0) GO TO 21		!Try again.        STOP "Oh well."			!Or quit, on negative vibrations.      END IF			!So much for selecting a piece.Complete the selected move.   30 BOARD(ZC,ZR) = IT		!Place the named piece where the space was.      LOCZ = LOCM(:,I)		!The space is now where that piece came from.      BOARD(ZC,ZR) = SPACE		!And now holds a space.c      write (6,*)c     1 "disorder=",COUNT(BORED(1:N - 2) + 1 .NE. BORED(2:N - 1))      IF (TRY.LE.6) WRITE (MSG,31)	!Set off with a nice heading.   31 FORMAT (/"The new layout...")	!Just for clarity.      CALL SHOW(NR,NC,BOARD)		!Perhaps it will be good.Check for success.      IF (BORED(N).NE.SPACE) GO TO 20	!Is the space at the end?      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: `WRITE (MSG,REC = 6) STUFF` would rewrite the sixth line of the display. Instead, output relentlessly rolls forwards, starting as follows:

```To play 'slide-square' with 4 rows and 4 columns.
The game is to slide a square into the space
(thus leaving a space behind) until you attain
the nice orderly layout as follows:

Row|__1__2__3__4
1|  1  2  3  4
2|  5  6  7  8
3|  9 10 11 12
4| 13 14 15  0

But, your board looks like this...
Row|__1__2__3__4
1| 15  0 14 11
2|  8 13  5  3
3|  4  1  7  9
4| 10  6  2 12
Moveable pieces: 14,13,15. Choose one: 15

The new layout...
Row|__1__2__3__4
1|  0 15 14 11
2|  8 13  5  3
3|  4  1  7  9
4| 10  6  2 12
Moveable pieces: 15,8. Choose one:
```

The display here turns out to be less rectangular than that of the "console" screen's usual setting, which changes with the typeface and sizing anyway. Endless variation. As for playing the game, it is much easier to get a "feel" for the possibilities when manipulating the actual physical object. The digital world is less real.

## FreeBASIC

`sub drawboard( B() as ubyte )    dim as string outstr = ""    for i as ubyte = 0 to 15        if B(i) = 0 then             outstr = outstr + " XX "        elseif B(i) < 10 then            outstr = outstr + "  "+str(B(i))+" "        else            outstr = outstr + " " +str(B(i))+" "        end if        if i mod 4 = 3 then             print outstr            print            outstr = ""        end if    next i    printend sub function move( B() as ubyte, byref gap as ubyte, direction as ubyte ) as ubyte    dim as integer targ = gap    select case direction        case 1  'UP            targ = gap - 4        case 2  'RIGHT            if gap mod 4 = 3 then return gap            targ = gap + 1        case 3  'DOWN            targ = gap + 4        case 4            if gap mod 4 = 0 then return gap            targ = gap - 1        case else            return gap    end select    if targ > 15 or targ < 0 then return gap    swap B(targ), B(gap)    return targend function sub shuffle( B() as ubyte, byref gap as ubyte )    for i as ubyte = 0 to 100        gap = move(B(), gap, int(rnd*4) + 1)    next iend sub function solved( B() as ubyte ) as boolean    dim as integer i    for i = 0 to 14        if B(i)>B(i+1) then return false    next i    return trueend function dim as ubyte i, B(15), direction, gapfor i = 0 to 15    B(i) = inext ishuffle B(), gap  while not solved( B() )    cls    drawboard B()    print gap    print "1 = up, 2=right, 3=down, 4=left"    input direction    gap = move( B(), gap, direction )wend clsprint "Congratulations! You win!"printdrawboard B()`

## Gambas

`'Charlie Ogier (C) 15PuzzleGame 24/04/2017 V0.1.0 Licenced under MIT'Inspiration came from: -''http://rosettacode.org/wiki/15_Puzzle_Game''Bugs or comments to [email protected]'Written in Gambas 3.9.2 - Updated on the Gambas Farm 01/05/2017'Updated so that the message and the Title show the same amount of moves 01/06/2017'Form now expandable. Font height automated. Form size and position saved 06/06/2107 'Simulate playing the 15 - game(puzzle)         Yes in GUI'Generate a random start position               Yes'Prompt the user for which piece To move        No'Validate if the move is legal(possible)        Yes'Display the game(puzzle) as pieces are moved   Yes in GUI'Announce when the puzzle is solved             Yes'Possibly keep track of the number of moves     Yes byPos As New Byte[]                                             'Stores the position of the 'Tiles'siMoves As Short                                                'Stores the amount of moveshTimer As Timer                                                 'TimerdTimerStart As Date                                             'Stores the start time dTimerDiff As Date                                              'Stores the time from the start to nowbTimerOn As Boolean                                             'To know if the Timer is running Public Sub Form_Open()                                          'Form opens Settings.read(Me, "Window")                                     'Get details of the last window position and sizeWith Me                                                         'With the Form..  .Padding = 5                                                  'Pad the edges  .Arrangement = Arrange.Row                                    'Arrange the Form  .Title = "15PuzzleGame v0.3.0"                                'Set the Form TitleEnd With BuildForm                                                       'Go to the BuildForm routine End Public Sub BuildForm()                                          'To add items to the FormDim hButton As Button                                           'We need some ButtonsDim byRand, byTest As Byte                                      'Various variablesDim bOK As Boolean                                              'Used to stop duplicate numbers being addedDim bSolvable As Boolean Repeat                                                          'Repeat until the puzzle is solvable      Do                                                            'Start of a Do loop to create 0 to 15 in random order    byRand = Rand(0, 15)                                        'Get a random number between 0 and 15    If byRand = 0 Then byRand = 99                              'Change 0 to 99 for the Blank space    bOK = True                                                  'Set bOK to True    For Each byTest In byPos                                    'For each number stored in the array byPos      If byRand = byTest Then bOK = False                       'Check to see if it already exists, if it does set bOK to False    Next    If bOK Then byPos.Add(byRand)                               'If not a duplicate then add it to the array    If byPos.max = 15 Then Break                                'Once the array has 16 numbers get out of here. 99 is used for the blank space  Loop  bSolvable = IsItSolvable()                                    'Go to the 'check if puzzle is solvable' routine  If Not bSolvable Then byPos.clear                             'If it's not solvable then clear byPosUntil bSolvable = True                                          'Repeat until the puzzle is solvable For byRand = 0 To 15                                            'Loop  If byPos[byRand] = 99 Then                                    'Check if value is 99 as this is where the blank space will go    AddPanel                                                    'Go to the AddPanel routine to add the blank space    Continue                                                    'Skip to the end of the loop  Endif  hButton = New Button(Me) As "AllButtons"                      'Add a new button to the Form, all buttons grouped as 'AllButtons'  With hButton                                                  'With the following properties    .Text = Str(byPos[byRand])                                  'Add Button text     .Tag = Str(byPos[byRand])                                   'Add a Tag    .Height = (Me.Height - 10) / 4                              'Set the Button height    .Width = (Me.Width - 10) / 4                                'Set the Button width    .Font.Bold = True                                           'Set the font to Bold    .Font.Size = 16                                             'Set Font size  End WithNext AddTimer                                                        'Go to the AddTimer routine End  Public Sub AddPanel()                                           'Routine to add an invisible panel that is the blank areaDim hPanel As Panel                                             'We need a Panel HPanel = New Panel(Me)                                          'Add the Panel to the FormWith HPanel                                                     'With the following Properties  .Tag = 99                                                     'Set a Tag to 99  .Height = (Me.Height - 10) / 4                                'Set the height  .Width = (Me.Width - 10) / 4                                  'Set the widthEnd With End Public Sub AddTimer()                                           'To add a Timer hTimer = New Timer As "MyTimer"                                 'Add a TimerhTimer.Delay = 1000                                             'Set the timer delay  End Public Sub MyTimer_Timer()                                      'Timer Me.Title = siMoves & " Moves "                                  'Set the Form Title to show the amount of moves taken If dTimerStart Then                                             'If a start time has been set then  dTimerDiff = Time(Now) - dTimerStart                          'Calculate the time difference between StartTime and Now  Me.Title &= " - " & Str(dTimerDiff)                           'Add the time taken to the Form TitleEnd If End Public Sub AllButtons_Click()                                   'What to do when a Button is clickedDim byLast As Byte = Last.Tag                                   'Get the Tag of the Button clickedDim byTemp, byCount As Byte                                     'Various variablesDim byCheck As Byte[] = [88, 88, 88, 88]                        'Used for checking for the blank spaceDim byWChgeck As New Byte[16, 4]Dim oObj As Object                                              'We need to enumerate Objects For Each oObj In Me.Children                                    'For each Object (Buttons in this case) that are Children of the Form..  If oObj.Tag = byLast Then Break                               'If the Tag of the Button matches then we know the position of the Button on the form so get out of here  Inc byCount                                                   'Increase the value of byCountNext Select Case byCount                                             'Depending on the position of the Button  Case 0                                                        'e.g 0 then we need to check positions 1 & 4 for the blank    byCheck[0] = 1    byCheck[1] = 4  Case 1    byCheck[0] = 0    byCheck[1] = 2    byCheck[2] = 5  Case 2    byCheck[0] = 1    byCheck[1] = 3    byCheck[2] = 6  Case 3    byCheck[0] = 2    byCheck[1] = 7  Case 4    byCheck[0] = 0    byCheck[1] = 5    byCheck[2] = 8  Case 5                                                        'e.g 5 then we need to check positions 1, 4, 6 & 9 for the blank    byCheck[0] = 1    byCheck[1] = 4    byCheck[2] = 6    byCheck[3] = 9  Case 6    byCheck[0] = 2    byCheck[1] = 5    byCheck[2] = 7    byCheck[3] = 10  Case 7    byCheck[0] = 3    byCheck[1] = 6    byCheck[2] = 11  Case 8    byCheck[0] = 4    byCheck[1] = 9    byCheck[2] = 12  Case 9    byCheck[0] = 5    byCheck[1] = 8    byCheck[2] = 10    byCheck[3] = 13  Case 10    byCheck[0] = 6    byCheck[1] = 9    byCheck[2] = 11    byCheck[3] = 14  Case 11    byCheck[0] = 7    byCheck[1] = 10    byCheck[2] = 15  Case 12    byCheck[0] = 8    byCheck[1] = 13  Case 13    byCheck[0] = 9    byCheck[1] = 12    byCheck[2] = 14  Case 14    byCheck[0] = 10    byCheck[1] = 13    byCheck[2] = 15  Case 15    byCheck[0] = 11    byCheck[1] = 14End Select For Each byTemp In byCheck                                      'For each value in byCheck  If byTemp = 88 Then Break                                     'If byTemp = 88 then get out of here  If byPos[byTemp] = 99 Then                                    'If the position checked is 99 (the blank) then..    byPos[byTemp] = Last.Text                                   'Set the new position of the Tile in byPos    byPos[byCount] = 99                                         'Set the existing Tile position to = 99 (blank)    Inc siMoves                                                 'Inc the amount of moves made    If Not bTimerOn Then                                        'If the Timer is now needed then       dTimerStart = Time(Now)                                   'Set the Start time to NOW      hTimer.start                                              'Start the Timer      bTimerOn = True                                           'Set bTimerOn to True     Endif    Break                                                       'Get out of here  End IfNext RebuildForm                                                     'Go to the RebuilForm routineCheckIfPuzzleCompleted                                          'Check to see if the puzzle has been solved End Public Sub CheckIfPuzzleCompleted()                             'Is the puzzle is completeDim byTest As Byte[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 99] 'byPos will equal this if it is completedDim siCount As Short                                            'CounterDim bCompleted As Boolean = True                                'Completed?Dim sMessage As String                                          'String to store the display message For siCount = 0 To 15                                           'Loop through the byPos  If byPos[siCount] <> byTest[siCount] Then                     'If the position does not match the completed position then..    bCompleted = False                                          'Set bCompleted to False    Break                                                       'Get out of here  EndifNext If bCompleted Then                                              'If the puzzle is completed then  hTimer.Stop                                                   'Stop the timer  Me.Title = siMoves & " Moves "                                'Set the Form Title to show the amount of moves taken  sMessage = "Congratulations!!\n"                              'Build sMessage  sMessage &= Str(siMoves) & " Moves\n"                         'Build sMessage  sMessage &= "Time = " & Str(dTimerDiff)                       'Build sMessage  Message(sMessage, "OK")                                       'Put up a congratulatory message  Me.Close                                                      'Close the formEndif End Public Sub RebuildForm()                                        'To clear the Form and rebuild with the Tiles in the new postionDim hButton As Button                                           'We need ButtonsDim byCount, byTemp As Byte                                     'Various variablesDim siFontH As Short Me.Children.clear                                               'Clear the Form of all Objects For Each byTemp In byPos                                        'For each 'Position'  If byTemp = 99 Then                                           'If the Position's value is 99 then it's the space    AddPanel                                                    'Go to the AddPanel routine  Else                                                          'If the Position's value is NOT 99 then     hButton = New Button(Me) As "AllButtons"                    'Create a new Button  With hButton                                                  'With the following properties    .Text = Str(byPos[byCount])                                 'Text as stored in byPos    .Tag = Str(byPos[byCount])                                  'Tag as stored in byPos    .Height = (Me.Height - 10) / 4                              'Set the Button height    .Width = (Me.Width - 10) / 4                                'Set the Button width    .Font.Bold = True                                           'Set the Font to Bold      End With    If Me.Width > Me.Height Then                                'If Form Width is greater than Form Width then..      siFontH = Me.Height                                       'siFontH = Form Height    Else                                                        'Else..      siFontH = Me.Width                                        'siFontH = Form Width    End If  hButton.Font.size = siFontH / 16                              'Set Font height  Endif   Inc byCount                                                   'Increase counterNext End Public Sub Form_Resize()                                        'If the form is resized RebuildForm                                                     'Rebuild the Form End Public Sub IsItSolvable() As Boolean                            'To check if the puzzle is solvableDim bSolvable, bBlankOnEven As Boolean                          'TriggersDim siCount0, siCount1, siInversion As Short                    'Counters For siCount0 = 0 To byPos.Max                                   'Loop through the positions  If byPos[siCount0] = 99 Then                                  'The blank    If InStr("0,1,2,3,8,9,10,11,", Str(siCount0 & ",")) Then    'Is the blank on an even row (counting from the bottom) if so..      bBlankOnEven = True                                       'bBlankOnEven = True    End If    Continue                                                    'Go to the end of the loop  End If  For siCount1 = siCount0 + 1 To byPos.Max                      'Loop through the positions     If byPos[siCount0] > byPos[siCount1] Then Inc siInversion   'Counts the inversions  Next                                                          'See https://www.cs.bham.ac.uk/~mdr/teaching/modules04/java2/TilesSolvability.htmlNext If bBlankOnEven And Odd(siInversion) Then bSolvable = True      'Blank is on an even row (counting from the bottom) then the number of inversions in a solvable situation is oddIf Not bBlankOnEven And Even(siInversion) Then bSolvable = True 'Blank is on an odd row (counting from the bottom) then the number of inversions in a solvable situation is even Return bSolvable                                                'Return the value End Public Sub Form_Close() Settings.Write(Me, "Window")                                    'Store the window position and size End `

## Go

`package main import (	"fmt"	"math/rand"	"strings"	"time") func main() {	rand.Seed(time.Now().UnixNano())	p := newPuzzle()	p.play()} type board [16]celltype cell uint8type move uint8 const (	up move = iota	down	right	left) func randMove() move { return move(rand.Intn(4)) } var solvedBoard = board{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0} func (b *board) String() string {	var buf strings.Builder	for i, c := range b {		if c == 0 {			buf.WriteString("  .")		} else {			_, _ = fmt.Fprintf(&buf, "%3d", c)		}		if i%4 == 3 {			buf.WriteString("\n")		}	}	return buf.String()} type puzzle struct {	board board	empty int // board[empty] == 0	moves int	quit  bool} func newPuzzle() *puzzle {	p := &puzzle{		board: solvedBoard,		empty: 15,	}	// Could make this configurable, 10==easy, 50==normal, 100==hard	p.shuffle(50)	return p} func (p *puzzle) shuffle(moves int) {	// As with other Rosetta solutions, we use some number	// of random moves to "shuffle" the board.	for i := 0; i < moves || p.board == solvedBoard; {		if p.doMove(randMove()) {			i++		}	}} func (p *puzzle) isValidMove(m move) (newIndex int, ok bool) {	switch m {	case up:		return p.empty - 4, p.empty/4 > 0	case down:		return p.empty + 4, p.empty/4 < 3	case right:		return p.empty + 1, p.empty%4 < 3	case left:		return p.empty - 1, p.empty%4 > 0	default:		panic("not reached")	}} func (p *puzzle) doMove(m move) bool {	i := p.empty	j, ok := p.isValidMove(m)	if ok {		p.board[i], p.board[j] = p.board[j], p.board[i]		p.empty = j		p.moves++	}	return ok} func (p *puzzle) play() {	fmt.Printf("Starting board:")	for p.board != solvedBoard && !p.quit {		fmt.Printf("\n%v\n", &p.board)		p.playOneMove()	}	if p.board == solvedBoard {		fmt.Printf("You solved the puzzle in %d moves.\n", p.moves)	}} func (p *puzzle) playOneMove() {	for {		fmt.Printf("Enter move #%d (U, D, L, R, or Q): ", p.moves+1)		var s string		if n, err := fmt.Scanln(&s); err != nil || n != 1 {			continue		} 		s = strings.TrimSpace(s)		if s == "" {			continue		} 		var m move		switch s[0] {		case 'U', 'u':			m = up		case 'D', 'd':			m = down		case 'L', 'l':			m = left		case 'R', 'r':			m = right		case 'Q', 'q':			fmt.Printf("Quiting after %d moves.\n", p.moves)			p.quit = true			return		default:			fmt.Println(`Please enter "U", "D", "L", or "R" to move the empty cellup, down, left, or right. You can also enter "Q" to quit.Upper or lowercase is accepted and only the first non-blankcharacter is important (i.e. you may enter "up" if you like).`)			continue		} 		if !p.doMove(m) {			fmt.Println("That is not a valid move at the moment.")			continue		} 		return	}}`

## Harbour

`  #include "inkey.ch"#include "Box.ch"  procedure Main()    // console init    SET SCOREBOARD OFF    SetMode(30,80)     ret := 0     // main loop    yn := .F.      DO WHILE yn == .F.        // draw console        cls        @ 0, 0 TO MaxRow(), MaxCol() DOUBLE        SetColor("BG+/B,W/N")        @ 0, 4 SAY "  Slidng puzzle game  "        SetColor()         // input size of grid        tam := 0                   @ MaxRow() - 2, 4 SAY "Size of grid: " GET tam PICTURE "9"        READ          // Initialize numbers        lista := ARRAY (tam * tam)        FOR z := 1 TO tam * tam            lista[z] := z        NEXT        lista1 := lista        grid := ARRAY (tam,tam)         // populate grid with numbers        FOR i := 1 TO tam             FOR j := 1 TO tam                 grid[i,j] := lista1[ (i-1) * tam + j]             NEXT         NEXT        Mostra(@grid)        InKey(0)         // initialize the game        n := 0        t := 0        lista := lista1     // lista for scrambling, lista1 preserve numbers in order        DO WHILE .T.            // scrambling numbers            FOR i := 1 TO tam*tam                n := Int ( ft_Rand1(tam * tam - 1) + 1 )                t := lista[n]                lista[n] := lista[i]                lista[i] := t            NEXT            // have solution?            possp := 0            invct := 0  // change counter            FOR i := 1 TO tam * tam -1                IF lista[i] != tam*tam                     FOR j := i + 1 TO tam * tam                         IF lista[j] != tam*tam                            IF lista[i] > lista[j]                                invct++                             ENDIF                         ENDIF                    NEXT                ELSE                    possp := i                ENDIF            NEXT            linv := If( ( (invct % 2) == 0 ), .T., .F.)            lkin := If( ( (tam - Int( (possp -1) / tam )) % 2) == 0, .T., .F. )             IF ( (tam % 2) != 0)    // if grid size is odd                IF linv                 // if number of positions changes is even, solvable                    EXIT                ELSE                     LOOP                // if is odd, not solvable, scramble more                ENDIF               // if grid size is even            ELSE                                                            // If changes is even and space position is in odd line                                        // or changes is odd and space position is in even line                                        // (XOR condition) is solvable                 IF (linv .AND. !lkin) .OR. (!linv .AND. lkin) // XOR !!!                     EXIT                ElSE                    // else scramble more                    LOOP                    ENDIF            ENDIF         ENDDO         // populate the grid with scrambled numbers        FOR i := 1 TO tam             FOR j := 1 TO tam                 grid[i,j] := lista[ (i-1) * tam + j]             NEXT         NEXT        ret := Mostra(@grid)     // play        key := 0        DO WHILE LastKey() != K_ESC            key := 0             // find the space coords            xe := 0            ye := 0            lv := tam*tam            FOR i := 1 TO tam                 FOR j := 1 TO tam                     IF grid[i,j] == lv                         xe :=i                        ye :=j                    ENDIF                NEXT            NEXT            // the direction keys            key := inkey(0)            DO CASE                 CASE key == K_UP                    IF xe > 1                        grid[xe,ye] := grid[xe-1,ye]                        grid[xe-1,ye] := lv                    ENDIF                    ret := Mostra(@grid)                            CASE key == K_DOWN                    IF xe < tam                        grid[xe,ye] := grid[xe+1,ye]                        grid[xe+1,ye] := lv                    ENDIF                    ret := Mostra(@grid)                   CASE key == K_LEFT                    IF ye > 1                        grid[xe,ye] := grid[xe,ye-1]                        grid[xe,ye-1] := lv                    ENDIF                    ret := Mostra(@grid)                   CASE key == K_RIGHT                    IF ye < tam                        grid[xe,ye] := grid[xe,ye+1]                        grid[xe,ye+1] := lv                    ENDIF                    ret := Mostra(@grid)               ENDCASE              IF ret == tam*tam-1                             // ret is qtty numbers in position                @ MaxRow() - 3, 4 SAY "Fim de jogo!"        // if ret == (size*size) -1                key := K_ESC                                // all numbers in position                EXIT                                        // game solved            ENDIF        ENDDO        @ MaxRow() - 2, 4 SAY "Deseja sair? (yn): " GET yn PICTURE "Y"        READ         @ MaxRow() - 3, 4 SAY "              "    ENDDOreturn NIL FUNCTION Mostra(grid)    // Show the gris    fim := 0                                                    // how many numbers in position?    SetColor("BG+/B,W/N")     @ 5,10 , 5 + tam * 2, 9 + tam * 4 BOX B_SINGLE + Space(1)    i := 0    FOR y := 1 TO tam        FOR x := 1 TO tam            IF grid[x,y] == tam * tam                           // show space                SetColor(" B/GR+, W/N")                @ x*2 + 4, i + 11 SAY "  "                 SetColor("BG+/B,W/N")            ELSE                 IF ( (x-1) * tam + y ) == grid[x,y]             // show number in position                    SetColor("W/G,W/N")                    @ x*2 + 4, i + 11 SAY grid[x,y] PICTURE "99"                     fim++                ELSE                                            // show number out position                    SetColor("BG+/B,W/N")                    @ x*2 + 4, i + 11 SAY grid[x,y] PICTURE "99"                ENDIF            ENDIF        NEXT        i = i + 4    NEXT    SetColor(" W/N, BG+/B")RETURN fim `

`import Data.Arrayimport System.Random type Puzzle = Array (Int, Int) Int main :: IO ()main = do    putStrLn "Please enter the difficulty level: 0, 1 or 2"    userInput <- getLine    let diffLevel = read userInput    if userInput == "" || any (\c -> c < '0' || c > '9') userInput || diffLevel > 2 || diffLevel < 0        then putStrLn "That is not a valid difficulty level." >> main        else shufflePuzzle ([10, 50, 100] !! diffLevel) solvedPuzzle >>= gameLoop gameLoop :: Puzzle -> IO ()gameLoop puzzle    | puzzle == solvedPuzzle = putStrLn "You solved the puzzle!" >> printPuzzle puzzle    | otherwise = do    printPuzzle puzzle    putStrLn "Please enter number to move"    userInput <- getLine    if any (\c -> c < '0' || c > '9') userInput        then putStrLn "That is not a valid number." >> gameLoop puzzle        else let move = read userInput in            if move `notElem` validMoves puzzle                then putStrLn "This move is not available." >> gameLoop puzzle                else gameLoop (applyMove move puzzle) validMoves :: Puzzle -> [Int]validMoves puzzle = [puzzle ! (row', column') |                     row' <- [rowEmpty-1..rowEmpty+1], column' <- [columnEmpty-1..columnEmpty+1],                     row' < 4, row' >= 0, column' < 4, column' >= 0,                     (row' == rowEmpty) /= (column' == columnEmpty)]    where (rowEmpty, columnEmpty) = findIndexOfNumber 16 puzzle applyMove :: Int -> Puzzle -> PuzzleapplyMove numberToMove puzzle = puzzle // [(indexToMove, 16), (emptyIndex, numberToMove)]    where indexToMove = findIndexOfNumber numberToMove puzzle          emptyIndex = findIndexOfNumber 16 puzzle findIndexOfNumber :: Int -> Puzzle -> (Int, Int)findIndexOfNumber number puzzle = case filter (\idx -> number == puzzle ! idx)                                              (indices puzzle) of                                      [idx] -> idx                                      _ -> error "BUG: number not in puzzle" printPuzzle :: Puzzle -> IO ()printPuzzle puzzle = do    putStrLn "+--+--+--+--+"    putStrLn \$ "|" ++ formatCell (0, 0) ++ "|" ++ formatCell (0, 1) ++ "|" ++ formatCell (0, 2) ++ "|" ++ formatCell (0, 3) ++ "|"    putStrLn "+--+--+--+--+"    putStrLn \$ "|" ++ formatCell (1, 0) ++ "|" ++ formatCell (1, 1) ++ "|" ++ formatCell (1, 2) ++ "|" ++ formatCell (1, 3) ++ "|"    putStrLn "+--+--+--+--+"    putStrLn \$ "|" ++ formatCell (2, 0) ++ "|" ++ formatCell (2, 1) ++ "|" ++ formatCell (2, 2) ++ "|" ++ formatCell (2, 3) ++ "|"    putStrLn "+--+--+--+--+"    putStrLn \$ "|" ++ formatCell (3, 0) ++ "|" ++ formatCell (3, 1) ++ "|" ++ formatCell (3, 2) ++ "|" ++ formatCell (3, 3) ++ "|"    putStrLn "+--+--+--+--+"    where formatCell idx              | i == 16 = "  "              | i > 9 = show i              | otherwise = " " ++ show i              where i = puzzle ! idx solvedPuzzle :: PuzzlesolvedPuzzle = listArray ((0, 0), (3, 3)) [1..16] shufflePuzzle :: Int -> Puzzle -> IO PuzzleshufflePuzzle 0 puzzle = return puzzleshufflePuzzle numOfShuffels puzzle = do    let moves = validMoves puzzle    randomIndex <- randomRIO (0, length moves - 1)    let move = moves !! randomIndex    shufflePuzzle (numOfShuffels - 1) (applyMove move puzzle)`

Output:

```Please enter the difficulty level: 0, 1 or 2
0
+--+--+--+--+
| 1| 6| 2| 4|
+--+--+--+--+
| 5|10| 3| 8|
+--+--+--+--+
| 9|14| 7|11|
+--+--+--+--+
|13|  |15|12|
+--+--+--+--+
14
+--+--+--+--+
| 1| 6| 2| 4|
+--+--+--+--+
| 5|10| 3| 8|
+--+--+--+--+
| 9|  | 7|11|
+--+--+--+--+
|13|14|15|12|
+--+--+--+--+

## J

Implementation:

`require'general/misc/prompt' genboard=:3 :0  b=. ?~16  if. 0<C.!.2 b do.    b=. (<0 _1)C. b  end.  a: (b i.0)} <"0 b) done=: (<"0]1+i.15),a: shift=: |.!._"0 2taxi=: |:,/"2(_1 1 shift i.4 4),_1 1 shift"0 1/ i.4 4 showboard=:3 :0  echo 'current board:'  echo 4 4\$y) help=:0 :0   Slide a number block into the empty space  until you get:┌──┬──┬──┬──┐│1 │2 │3 │4 │├──┼──┼──┼──┤│5 │6 │7 │8 │├──┼──┼──┼──┤│9 │10│11│12│├──┼──┼──┼──┤│13│14│15│  │└──┴──┴──┴──┘  Or type 'q' to quit.) getmove=:3 :0  showboard y  blank=. y i. a:  options=. /:~ ;y {~ _ -.~ blank { taxi  whilst. -. n e. options do.    echo 'Valid moves: ',":options    t=. prompt 'move which number? '    if. 'q' e. t do.      echo 'giving up'      throw.    elseif. 'h' e. t do.      echo help      showboard y    end.    n=. {._".t  end.  (<blank,y i.<n) C. y) game=: 3 :0  echo '15 puzzle'  echo 'h for help, q to quit'  board=. genboard''  whilst. -. done-:board do.    board=. getmove board  end.  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.

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:

`   game''15 puzzleh for help, q to quitcurrent board:┌──┬──┬──┬──┐│1 │2 │3 │4 │├──┼──┼──┼──┤│5 │6 │7 │8 │├──┼──┼──┼──┤│9 │10│  │11│├──┼──┼──┼──┤│13│14│15│12│└──┴──┴──┴──┘Valid moves: 7 10 11 15move which number? 11current board:┌──┬──┬──┬──┐│1 │2 │3 │4 │├──┼──┼──┼──┤│5 │6 │7 │8 │├──┼──┼──┼──┤│9 │10│11│  │├──┼──┼──┼──┤│13│14│15│12│└──┴──┴──┴──┘Valid moves: 8 11 12move which number? 12current board:┌──┬──┬──┬──┐│1 │2 │3 │4 │├──┼──┼──┼──┤│5 │6 │7 │8 │├──┼──┼──┼──┤│9 │10│11│12│├──┼──┼──┼──┤│13│14│15│  │└──┴──┴──┴──┘You win.`

## Java

Works with: Java version 8
`package fifteenpuzzle; import java.awt.*;import java.awt.event.*;import java.util.Random;import javax.swing.*; class FifteenPuzzle extends JPanel {     private final int side = 4;    private final int numTiles = side * side - 1;     private final Random rand = new Random();    private final int[] tiles = new int[numTiles + 1];    private final int tileSize;    private int blankPos;    private final int margin;    private final int gridSize;    private boolean gameOver;     private FifteenPuzzle() {        final int dim = 640;         margin = 80;        tileSize = (dim - 2 * margin) / side;        gridSize = tileSize * side;         setPreferredSize(new Dimension(dim, dim + margin));        setBackground(Color.WHITE);        setForeground(new Color(0x6495ED)); // cornflowerblue        setFont(new Font("SansSerif", Font.BOLD, 60));         gameOver = true;         addMouseListener(new MouseAdapter() {            @Override            public void mousePressed(MouseEvent e) {                if (gameOver) {                    newGame();                 } else {                     int ex = e.getX() - margin;                    int ey = e.getY() - margin;                     if (ex < 0 || ex > gridSize || ey < 0 || ey > gridSize) {                        return;                    }                     int c1 = ex / tileSize;                    int r1 = ey / tileSize;                    int c2 = blankPos % side;                    int r2 = blankPos / side;                     int clickPos = r1 * side + c1;                     int dir = 0;                    if (c1 == c2 && Math.abs(r1 - r2) > 0) {                        dir = (r1 - r2) > 0 ? 4 : -4;                     } else if (r1 == r2 && Math.abs(c1 - c2) > 0) {                        dir = (c1 - c2) > 0 ? 1 : -1;                    }                     if (dir != 0) {                        do {                            int newBlankPos = blankPos + dir;                            tiles[blankPos] = tiles[newBlankPos];                            blankPos = newBlankPos;                        } while (blankPos != clickPos);                        tiles[blankPos] = 0;                    }                     gameOver = isSolved();                }                repaint();            }        });         newGame();    }     private void newGame() {        do {            reset();            shuffle();        } while (!isSolvable());        gameOver = false;    }     private void reset() {        for (int i = 0; i < tiles.length; i++) {            tiles[i] = (i + 1) % tiles.length;        }        blankPos = tiles.length - 1;    }     private void shuffle() {        // don't include the blank space in the shuffle, leave it        // in the home position        int n = numTiles;        while (n > 1) {            int r = rand.nextInt(n--);            int tmp = tiles[r];            tiles[r] = tiles[n];            tiles[n] = tmp;        }    }     /*  Only half the permutations of the puzzle are solvable.         Whenever a tile is preceded by a tile with higher value it counts        as an inversion. In our case, with the blank space in the home        position, the number of inversions must be even for the puzzle        to be solvable.         See also:        www.cs.bham.ac.uk/~mdr/teaching/modules04/java2/TilesSolvability.html     */    private boolean isSolvable() {        int countInversions = 0;        for (int i = 0; i < numTiles; i++) {            for (int j = 0; j < i; j++) {                if (tiles[j] > tiles[i]) {                    countInversions++;                }            }        }        return countInversions % 2 == 0;    }     private boolean isSolved() {        if (tiles[tiles.length - 1] != 0) {            return false;        }        for (int i = numTiles - 1; i >= 0; i--) {            if (tiles[i] != i + 1) {                return false;            }        }        return true;    }     private void drawGrid(Graphics2D g) {        for (int i = 0; i < tiles.length; i++) {            int r = i / side;            int c = i % side;            int x = margin + c * tileSize;            int y = margin + r * tileSize;             if (tiles[i] == 0) {                if (gameOver) {                    g.setColor(Color.GREEN);                    drawCenteredString(g, "\u2713", x, y);                }                continue;            }             g.setColor(getForeground());            g.fillRoundRect(x, y, tileSize, tileSize, 25, 25);            g.setColor(Color.blue.darker());            g.drawRoundRect(x, y, tileSize, tileSize, 25, 25);            g.setColor(Color.WHITE);             drawCenteredString(g, String.valueOf(tiles[i]), x, y);        }    }     private void drawStartMessage(Graphics2D g) {        if (gameOver) {            g.setFont(getFont().deriveFont(Font.BOLD, 18));            g.setColor(getForeground());            String s = "click to start a new game";            int x = (getWidth() - g.getFontMetrics().stringWidth(s)) / 2;            int y = getHeight() - margin;            g.drawString(s, x, y);        }    }     private void drawCenteredString(Graphics2D g, String s, int x, int y) {        FontMetrics fm = g.getFontMetrics();        int asc = fm.getAscent();        int des = fm.getDescent();         x = x + (tileSize - fm.stringWidth(s)) / 2;        y = y + (asc + (tileSize - (asc + des)) / 2);         g.drawString(s, x, y);    }     @Override    public void paintComponent(Graphics gg) {        super.paintComponent(gg);        Graphics2D g = (Graphics2D) gg;        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                RenderingHints.VALUE_ANTIALIAS_ON);         drawGrid(g);        drawStartMessage(g);    }     public static void main(String[] args) {        SwingUtilities.invokeLater(() -> {            JFrame f = new JFrame();            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);            f.setTitle("Fifteen Puzzle");            f.setResizable(false);            f.add(new FifteenPuzzle(), BorderLayout.CENTER);            f.pack();            f.setLocationRelativeTo(null);            f.setVisible(true);        });    }}`

## JavaScript

Play it here

` var board, zx, zy, clicks, possibles, clickCounter, oldzx = -1, oldzy = -1;function getPossibles() {    var ii, jj, cx = [-1, 0, 1, 0], cy = [0, -1, 0, 1];    possibles = [];    for( var i = 0; i < 4; i++ ) {        ii = zx + cx[i]; jj = zy + cy[i];        if( ii < 0 || ii > 3 || jj < 0 || jj > 3 ) continue;        possibles.push( { x: ii, y: jj } );    }}function updateBtns() {    var b, v, id;    for( var j = 0; j < 4; j++ ) {        for( var i = 0; i < 4; i++ ) {            id = "btn" + ( i + j * 4 );            b = document.getElementById( id );            v = board[i][j];            if( v < 16 ) {                b.innerHTML = ( "" + v );                b.className = "button"            }            else {                b.innerHTML = ( "" );                b.className = "empty";            }        }    }    clickCounter.innerHTML = "Clicks: " + clicks;}function shuffle() {    var v = 0, t;     do {        getPossibles();        while( true ) {            t = possibles[Math.floor( Math.random() * possibles.length )];            console.log( t.x, oldzx, t.y, oldzy )            if( t.x != oldzx || t.y != oldzy ) break;        }        oldzx = zx; oldzy = zy;        board[zx][zy] = board[t.x][t.y];        zx = t.x; zy = t.y;        board[zx][zy] = 16;     } while( ++v < 200 );}function restart() {    shuffle();    clicks = 0;    updateBtns();}function checkFinished() {    var a = 0;    for( var j = 0; j < 4; j++ ) {        for( var i = 0; i < 4; i++ ) {            if( board[i][j] < a ) return false;            a = board[i][j];        }    }    return true;}function btnHandle( e ) {    getPossibles();    var c = e.target.i, r = e.target.j, p = -1;    for( var i = 0; i < possibles.length; i++ ) {        if( possibles[i].x == c && possibles[i].y == r ) {            p = i;            break;        }    }    if( p > -1 ) {        clicks++;        var t = possibles[p];        board[zx][zy] = board[t.x][t.y];        zx = t.x; zy = t.y;        board[zx][zy] = 16;        updateBtns();        if( checkFinished() ) {            setTimeout(function(){                 alert( "WELL DONE!" );                restart();            }, 1);        }    }}function createBoard() {    board = new Array( 4 );    for( var i = 0; i < 4; i++ ) {        board[i] = new Array( 4 );    }    for( var j = 0; j < 4; j++ ) {        for( var i = 0; i < 4; i++ ) {            board[i][j] = ( i + j * 4 ) + 1;        }    }    zx = zy = 3; board[zx][zy] = 16;}function createBtns() {    var b, d = document.createElement( "div" );    d.className += "board";    document.body.appendChild( d );    for( var j = 0; j < 4; j++ ) {        for( var i = 0; i < 4; i++ ) {            b = document.createElement( "button" );            b.id = "btn" + ( i + j * 4 );            b.i = i; b.j = j;            b.addEventListener( "click", btnHandle, false );            b.appendChild( document.createTextNode( "" ) );            d.appendChild( b );        }    }    clickCounter = document.createElement( "p" );    clickCounter.className += "txt";    document.body.appendChild( clickCounter );}function start() {    createBtns();    createBoard();    restart();} `

Html to test

```<!DOCTYPE html>
<title>15 Puzzle</title>
<script src="p15.js"></script>
<style>
.txt{color:#fff;text-align:center;font-size:5vh}
.empty{background:#333;border:1px solid #111}
</style>
```

## Julia

` using Random const size = 4const puzzle = string.(reshape(1:16, size, size))puzzle[16] = " "rng = MersenneTwister(Int64(round(time())))shufflepuzzle() = (puzzle .= shuffle(rng, puzzle))findtile(t) = findfirst(x->x == t, puzzle)findhole() = findtile(" ") function issolvable()    inversioncount = 1    asint(x) = (puzzle[x] == " ") ? 0 : parse(Int64, puzzle[x])    for i in 1:size^2-1, j in i:size^2        if puzzle[i] == " " || puzzle[j] == " "            continue        end        if parse(Int, puzzle[i]) < parse(Int, puzzle[j])            inversioncount += 1        end    end    if size % 2 == 1        return inversioncount % 2 == 0    end    pos = findhole()    inversioncount += pos[2]    return inversioncount & 1 == 0end function nexttohole()    holepos = findhole()    row = holepos[1]    col = holepos[2]    if row == 1        if col == 1            return [[row, col + 1], [row + 1, col]]        elseif col == size            return [[row, col - 1], [row + 1, col]]        else            return [[row, col - 1], [row, col + 1], [row + 1, col]]        end    elseif row == size        if col == 1            return [[row - 1, col], [row, col + 1]]        elseif col == size            return [[row - 1, col], [row, col - 1]]        else            return [[row - 1, col], [row, col - 1], [row, col + 1]]        end    else        if col == 1            return [[row - 1, col], [row, col + 1], [row + 1, col]]        elseif col == size            return [[row - 1, col], [row, col - 1], [row + 1, col]]        else            return [[row - 1, col], [row, col - 1], [row, col + 1], [row + 1, col]]        end    endend possiblemoves() = map(pos->puzzle[pos[1], pos[2]], nexttohole()) function movehole(tiletofill)    if tiletofill in possiblemoves()        curpos = findtile(tiletofill)        holepos = findhole()        puzzle[holepos] = tiletofill        puzzle[curpos] = " "    else        println("Bad tile move \$tiletofill.\nPossible moves are \$(possiblemoves()).")    endend function printboard()    ppuz(x,y) = print(lpad(rpad(puzzle[x,y], 3), 4), "|")    print("+----+----+----+----+\n|")    for j in 1:size, i in 1:size        ppuz(i,j)        if i == size            print("\n")            if j < size                 print("|")            end        end    end    println("+----+----+----+----+") end function puzzle15play()    solved() = (puzzle[1:15] == string.(1:15))    shufflepuzzle()    println("This puzzle is", issolvable() ? " " : " not ", "solvable.")    while !solved()        printboard()        print("Possible moves are: \$(possiblemoves()), 0 to exit. Your move? =>  ")        s = readline()        if s == "0"            exit(0)        end        movehole(s)    end    printboard()    println("Puzzle solved.")end puzzle15play() `
Output:
```
This puzzle is solvable.
+----+----+----+----+
| 5  | 7  | 14 |    |
| 4  | 3  | 13 | 12 |
| 10 | 1  | 6  | 9  |
| 8  | 15 | 11 | 2  |
+----+----+----+----+
Possible moves are: ["14", "12"], 0 to exit. Your move? =>  12
+----+----+----+----+
| 5  | 7  | 14 | 12 |
| 4  | 3  | 13 |    |
| 10 | 1  | 6  | 9  |
| 8  | 15 | 11 | 2  |
+----+----+----+----+
Possible moves are: ["13", "12", "9"], 0 to exit. Your move? =>

```

## Kotlin

Translation of: Java
`// version 1.1.3 import java.awt.BorderLayoutimport java.awt.Colorimport java.awt.Dimensionimport java.awt.Fontimport java.awt.Graphicsimport java.awt.Graphics2Dimport java.awt.RenderingHintsimport java.awt.event.MouseAdapterimport java.awt.event.MouseEventimport java.util.Randomimport javax.swing.JFrameimport javax.swing.JPanelimport javax.swing.SwingUtilities class FifteenPuzzle(dim: Int, val margin: Int) : JPanel() {     private val rand = Random()    private val tiles = IntArray(16)    private val tileSize = (dim - 2 * margin) / 4    private val gridSize = tileSize * 4    private var blankPos = 0     init {        preferredSize = Dimension(dim, dim)        background = Color.white        val cornflowerBlue = 0x6495ED        foreground = Color(cornflowerBlue)        font = Font("SansSerif", Font.BOLD, 60)         addMouseListener(object : MouseAdapter() {            override fun mousePressed(e: MouseEvent) {                val ex = e.x - margin                val ey = e.y - margin                if (ex !in 0..gridSize || ey !in 0..gridSize) return                 val c1 = ex / tileSize                val r1 = ey / tileSize                val c2 = blankPos % 4                val r2 = blankPos / 4                if ((c1 == c2 && Math.abs(r1 - r2) == 1) ||                    (r1 == r2 && Math.abs(c1 - c2) == 1)) {                    val clickPos = r1 * 4 + c1                    tiles[blankPos] = tiles[clickPos]                    tiles[clickPos] = 0                    blankPos = clickPos                }                repaint()            }        })         shuffle()    }     private fun shuffle() {        do {            reset()            // don't include the blank space in the shuffle,            // leave it in the home position            var n = 15            while (n > 1) {                val r = rand.nextInt(n--)                val tmp = tiles[r]                tiles[r] = tiles[n]                tiles[n] = tmp            }        } while (!isSolvable())    }     private fun reset() {        for (i in 0 until tiles.size) {            tiles[i] = (i + 1) % tiles.size        }        blankPos = 15    }     /*  Only half the permutations of the puzzle are solvable.         Whenever a tile is preceded by a tile with higher value it counts        as an inversion. In our case, with the blank space in the home        position, the number of inversions must be even for the puzzle        to be solvable.    */     private fun isSolvable(): Boolean {        var countInversions = 0        for (i in 0 until 15) {            (0 until i)                .filter { tiles[it] > tiles[i] }                .forEach { countInversions++ }        }        return countInversions % 2 == 0    }     private fun drawGrid(g: Graphics2D) {        for (i in 0 until tiles.size) {            if (tiles[i] == 0) continue             val r = i / 4            val c = i % 4            val x = margin + c * tileSize            val y = margin + r * tileSize             with(g) {                color = foreground                fillRoundRect(x, y, tileSize, tileSize, 25, 25)                color = Color.black                drawRoundRect(x, y, tileSize, tileSize, 25, 25)                color = Color.white            }            drawCenteredString(g, tiles[i].toString(), x, y)        }    }     private fun drawCenteredString(g: Graphics2D, s: String, x: Int, y: Int) {        val fm = g.fontMetrics        val asc = fm.ascent        val des = fm.descent         val xx = x + (tileSize - fm.stringWidth(s)) / 2        val yy = y + (asc + (tileSize - (asc + des)) / 2)         g.drawString(s, xx, yy)    }     override fun paintComponent(gg: Graphics) {        super.paintComponent(gg)        val g = gg as Graphics2D        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,            RenderingHints.VALUE_ANTIALIAS_ON)        drawGrid(g)    }} fun main(args: Array<String>) {    SwingUtilities.invokeLater {        val f = JFrame()        with(f) {            defaultCloseOperation = JFrame.EXIT_ON_CLOSE            title = "Fifteen Puzzle"            isResizable = false            add(FifteenPuzzle(640, 80), BorderLayout.CENTER)            pack()            setLocationRelativeTo(null)            isVisible = true        }    }}`

## Liberty BASIC

Translation of: Commodore BASIC
Works with: Just BASIC
` ' 15-PUZZLE GAME' ********************************dim d(16)dim ds\$(16) ' Board piecesdim sh(3) call buildBoard introAndLevel()call printPuzzledo  print "To move a piece, enter its number: "  input x  while isMoveValid(x, y, z) = 0    print "Wrong move."    call printPuzzle    print "To move a piece, enter its number: "    input x  wend  d(z) = x  d(y) = 0  call printPuzzleloop until isPuzzleComplete()print "YOU WON!"end sub printPuzzle  for p = 1 to 16    if d(p) = 0 then      ds\$(p) = "     "    else      ds\$(p) = using("###", d(p)) + "  "    end if  next p  print "+-----+-----+-----+-----+"  print "|"; ds\$(1); "|"; ds\$(2); "|"; ds\$(3); "|"; ds\$(4); "|"  print "+-----+-----+-----+-----+"  print "|"; ds\$(5); "|"; ds\$(6); "|"; ds\$(7); "|"; ds\$(8); "|"  print "+-----+-----+-----+-----+"  print "|"; ds\$(9); "|"; ds\$(10); "|";ds\$(11); "|"; ds\$(12); "|"  print "+-----+-----+-----+-----+"  print "|"; ds\$(13); "|"; ds\$(14); "|"; ds\$(15); "|"; ds\$(16); "|"  print "+-----+-----+-----+-----+"end sub function introAndLevel()  cls  sh(1) = 10  sh(2) = 50  sh(3) = 100  print "15 PUZZLE GAME"  print  print  print "Please enter level of difficulty,"  do    print "1 (easy), 2 (medium) or 3 (hard): ";    input level  loop while (level < 1) or (level > 3)  introAndLevel = levelend function sub buildBoard level  ' Set pieces in correct order first  for p = 1 to 15    d(p) = p  next p  d(16) = 0 ' 0 = empty piece/slot  z = 16 ' z = empty position  print  print "Shuffling pieces";  for n = 1 to sh(level)    print ".";    do      x = int(rnd(0) * 4) + 1      select case x        case 1          r = z - 4        case 2          r = z + 4        case 3          if (z - 1) mod 4 <> 0 then            r = z - 1          end if        case 4          if z mod 4 <> 0 then            r = z + 1          end if      end select    loop while (r < 1) or (r > 16)    d(z) = d(r)    z = r    d(z) = 0  next n  clsend sub function isMoveValid(piece, byref piecePos, byref emptyPos)  mv = 0  if (piece >= 1) and (piece <= 15) then    piecePos = piecePosition(piece)    emptyPos = piecePosition(0)    ' Check if empty piece is above, below, left or right to piece    if (piecePos - 4 = emptyPos) or _      (piecePos + 4 = emptyPos) or _      ((piecePos - 1 = emptyPos) and (emptyPos mod 4 <> 0)) or _      ((piecePos + 1 = emptyPos) and (piecePos mod 4 <> 0)) then      mv = 1    end if  end if  isMoveValid = mvend function function piecePosition(piece)  p = 1  while d(p) <> piece    p = p + 1    if p > 16 then      print "UH OH!"      stop    end if  wend  piecePosition = pend function function isPuzzleComplete()  pc = 0  p = 1  while (p < 16) and (d(p) = p)    p = p + 1  wend  if p = 16 then    pc = 1  end if  isPuzzleComplete = pcend function `

## LiveCode

` #Please note that all this code can be performed in livecode with just few mouse clicks#This is just a pure script exampeon OpenStack   show me  #Usually not necessary   #tile creation   set the width of the templateButton to 50   set the height of the templateButton to 50   repeat with i=1 to 16      create button       set the label of  button i to i      if i =1 then         set the top of button 1 to 0         set the left of button 1 to 0      end if      if i > 1 and 1 <=4  then         set the left of button i to the right of button (i-1)         set the top of button i to the top of button 1      end if      if i >= 5 and i <= 8 then         set the top of button i to the bottom of button 1         if i = 5 then             set the left of button i to the left of button 1         else            set the left of button i to the right of button (i - 1)         end if      end if         if i >= 9 and i <= 12 then         set the top of button i to the bottom of button 5         if i = 9 then             set the left of button i to the left of button 1         else             set the left of button i to the right of button (i - 1)         end if      end if         if i >= 13 and i <= 16 then         set the top of button i to the bottom of button 9         if i = 13 then             set the left of button i to the left of button 1         else             set the left of button i to the right of button (i - 1)         end if      end if         #this is usally the script directly wirtten in the objects, it's really weird this way      put "on MouseUp" &CR& "if checkDistance(the label of me) then" & CR &"put the loc of me into temp" into ts      put CR& "set the loc of me to the loc of button 16" after ts      put CR& "set the loc of button 16 to temp" & Cr & "end if " &CR &"End MouseUp" after ts      set the script of button i to ts   end repeat   #graphic adjustements   set the visible of button 16 to false      set the width of this stack to the right of button 16   set the height of this stack to the bottom of button 16end openStack function checkDistance i   if (((the top of button i - the bottom of button 16) = 0 OR (the top of button 16 - the bottom of button i) = 0) AND the left of button i = the left of button 16) OR (((the left of button i - the right of button 16) = 0 OR (the right of button i - the left of button 16) = 0) AND the top of button i = the top of button 16) then      return true   else       return false   end ifend checkDistance `

Screenshot: [2]

## Lua

` math.randomseed( os.time() )local puz = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }local dir = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }local sx, sy = 4, 4  function isValid( tx, ty )    return tx > 0 and tx < 5 and ty > 0 and ty < 5  endfunction display()    io.write( "\n\n" )    for j = 1, 4 do        io.write( "+----+----+----+----+\n" )        for i = 1, 4 do            local d = puz[i + j * 4 - 4]            io.write( ": " )            if d < 10 then io.write( " " ) end            if d < 1  then                io.write( "  " )            else                io.write( string.format( "%d ", d ) )            end        end        io.write( ":\n" )    end    io.write( "+----+----+----+----+\n\n" )endfunction getUserNove()    local moves, r, tx, ty = {}    for d = 1, 4 do        tx = sx; ty = sy        tx = tx + dir[d][1]; ty = ty + dir[d][2]         if isValid( tx, ty ) then             table.insert( moves, puz[tx + ty * 4 - 4] )        end    end     io.write( "Your possible moves are: " )    for i = 1, #moves do         io.write( string.format( "%d ", moves[i] ) )    end     io.write ( "\nYour move: " ); r = tonumber( io.read() )    if r ~= nil then        for i = 1, #moves do            if moves[i] == r then return r end        end    end    print( "Invalid move!" )    return -1endfunction checked( r )    for i = 1, #puz do        if puz[i] == r then             puz[i] = 0            sx = 1 + ( i - 1 ) % 4; sy = math.floor( ( i + 3 ) / 4 )        elseif puz[i] == 0 then            puz[i] = r        end    end    for i = 2, #puz - 1 do        if puz[i - 1] + 1 ~= puz[i] then return false end    end    return trueendfunction beginGame()    local r, d, tx, ty    while( true ) do        for i = 1, 100 do            while( true ) do                tx = sx; ty = sy; d = math.random( 4 )                tx = tx + dir[d][1]; ty = ty + dir[d][2]                if isValid( tx, ty ) then break end            end            puz[sx + sy * 4 - 4] = puz[tx + ty * 4 - 4]            puz[tx + ty * 4 - 4] = 0; sx = tx; sy = ty        end        while( true ) do            display(); r = getUserNove()            if r > 0 then                if checked( r ) then                    display()                    io.write( "\nDone!\n\nPlay again (Y/N)?" )                    r = io.read()                    if r ~= "Y" and r ~= "y" then                         return                    else                        break                    end                end            end        end    endend-- [ entry point ] --beginGame() `
Output:
```+----+----+----+----+
:  1 : 15 :  7 :  3 :
+----+----+----+----+
: 14 :  9 :  2 :  4 :
+----+----+----+----+
:  5 : 13 :    : 10 :
+----+----+----+----+
:  6 : 12 :  8 : 11 :
+----+----+----+----+

Your possible moves are: 10 13 8 2
```
Translation of: COMMODORE BASIC

## M2000 Interpreter

Translation of: COMMODORE BASIC

I make a function RND() and I set Base for arrays to 1 Also I put some semi colons to Print statement Also I have to put variables after NEXT statements To be actual BASIC compatible we have to set FOR NEXT like BASIC, because for M2000 the direction defined by the starting-ending values, so always we get at least one time the block between FOR NEXT. So we can use:

SET SWITCHES "+FOR"

But here we don't have to skip FOR NEXT and no STEP clause used.

Also the code is not the best, because we can move from 4 position to 5 (we can't do that with real puzzle)

` Module Puzzle15 {       00 BASE 1 : DEF RND(X)=RND      10 REM 15-PUZZLE GAME      20 REM COMMODORE BASIC 2.0      30 REM ********************************      40 GOSUB 400 : REM INTRO AND LEVEL      50 GOSUB 510 : REM SETUP BOARD      60 GOSUB 210 : REM PRINT PUZZLE      70 PRINT "TO MOVE A PIECE, ENTER ITS NUMBER:"      80 INPUT X      90 GOSUB 730 : REM CHECK IF MOVE IS VALID      100 IF MV=0 THEN PRINT "WRONG MOVE" : GOSUB 1050 : GOTO 60      110 D(Z)=X : D(Y)=0      120 GOSUB 210 : REM PRINT PUZZLE      130 GOSUB 950 : REM CHECK IF PUZZLE COMPLETE      140 IF PC THEN 160      150 GOTO 70      160 PRINT"YOU WON!"      170 END      180 REM      190 REM *******************************      200 REM PRINT/DRAW THE PUZZLE      210 FOR P=1 TO 16      220   IF D(P)=0 THEN D\$(P)="     " : GOTO 260      230   S\$=STR\$(D(P))      240   N=LEN(S\$)      250   D\$(P) = LEFT\$("   ",3-N)+S\$+"  "      260 NEXT P      270 PRINT "+-----+-----+-----+-----+"      280 PRINT "!";D\$(1);"!";D\$(2);"!";D\$(3);"!";D\$(4);"!"      290 PRINT "+-----+-----+-----+-----+"      300 PRINT "!";D\$(5);"!";D\$(6);"!";D\$(7);"!";D\$(8);"!"      310 PRINT "+-----+-----+-----+-----+"      320 PRINT "!";D\$(9);"!";D\$(10);"!";D\$(11);"!";D\$(12);"!"      330 PRINT "+-----+-----+-----+-----+"      340 PRINT "!";D\$(13);"!";D\$(14);"!";D\$(15);"!";D\$(16);"!"      350 PRINT "+-----+-----+-----+-----+"      360 RETURN      370 REM      380 REM *******************************      390 REM INTRO AND LEVEL OF DIFFICULTY      400 PRINT CHR\$(147)      410 DIM SH(3) : SH(1)=10 : SH(2)=50 : SH(3)=100      420 PRINT "15 PUZZLE GAME FOR COMMODORE BASIC 2.0" : PRINT : PRINT      430 PRINT "PLEASE ENTER LEVEL OF DIFFICULTY,"      440 PRINT "1(EASY), 2(MEDIUM) OR 3(HARD):";      450 INPUT V      460 IF V<1 OR V>3 THEN 440      470 RETURN      480 REM      490 REM *******************************      500 REM BUILD THE BOARD      510 DIM D(16) : DIM D\$(16) : REM BOARD PIECES      520 REM SET PIECES IN CORRECT ORDER FIRST      530 FOR P=1 TO 15      540   D(P) = P      550 NEXT P      560 D(16) = 0 : REM 0 = EMPTY PIECE/SLOT      570 Z=16      : REM Z = EMPTY POSITION      580 PRINT: PRINT "SHUFFLING PIECES";      590 FOR N=1 TO SH(V)      600   PRINT".";      610   X = INT(RND(0)*4)+1      620   R = Z+(X=1)*4-(X=2)*4+(X=3)-(X=4)      630   IF R<1 OR R>16 THEN 610      640   D(Z)=D(R)      650   Z=R      660   D(Z)=0      670 NEXT N      680 PRINT CHR\$(147)      690 RETURN      700 REM      710 REM *******************************      720 REM CHECK IF MOVE IS VALID      730 MV = 0      740 IF X<1 OR X>15 THEN RETURN      750 REM FIND POSITION OF PIECE X      760 P=1      770 IF D(P)=X THEN Y=P : GOTO 810      780 P=P+1 : IF P>16 THEN PRINT "UH OH!" : STOP      790 GOTO 770      800 REM FIND POSITION OF EMPTY PIECE      810 P=1      820 IF D(P)=0 THEN Z=P : GOTO 860      830 P=P+1 : IF P>16 THEN PRINT "UH OH!" : STOP      840 GOTO 820      850 PRINT Y;Z      860 REM CHECK IF EMPTY PIECE IS ABOVE, BELOW, LEFT OR RIGHT TO PIECE X      870 IF Y-4=Z THEN MV=1 : RETURN      880 IF Y+4=Z THEN MV=1 : RETURN      890 IF Y-1=Z THEN MV=1 : RETURN      900 IF Y+1=Z THEN MV=1 : RETURN      910 RETURN      920 REM      930 REM *******************************      940 REM CHECK IF PUZZLE IS COMPLETE / GAME OVER      950 PC = 0      960 P=1      970 IF D(P)<>P THEN RETURN      980 P=P+1      990 IF P<16 THEN 970      1000 PC = 1      1010 RETURN      1020 REM      1030 REM ******************************      1040 REM A SMALL DELAY      1050 FOR T=0 TO 400      1060 NEXT T      1070 RETURN}Puzzle15 `

## Mathematica / Wolfram Language

`grid = MapThread[{#1,#2} &, {Range @ 16, Range @ 16}] Move[x_] := (empty = Select[grid, #[[1]]==16 &][[1,2]];             If[(empty == x+4) || (empty == x-4) ||                (Mod[empty,4] != 0 && empty == x-1) ||                (Mod[empty,4] != 1 && empty == x+1),             oldEmpty = grid[[empty]][[1]];             grid[[empty]][[1]] = grid[[x]][[1]];             grid[[x]][[1]] = oldEmpty]) CButton[{x_,loc_}] := If[x==16, Null, Button[x,Move @ loc]] Dynamic @ Grid @ Partition[CButton /@ grid,4]`

## Mercury

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:

`:- module fifteen.:- interface.:- use_module random, io. :- type board.:- type invalid_board    --->    invalid_board(board).:- type move    --->    up    ;       down    ;       left    ;       right.     % init(Board):    % Board is fifteen game in its initial state    %:- pred init(board::out) is det.     % print(Board, !IO):- pred print(board::in, io.io::di, io.io::uo) is det.     % Shuffled(Board, !RS):    % Board is a fifteen game in a random (but valid) state.    %:- pred shuffled(board::out, random.supply::mdi, random.supply::muo) is det.     % space(Board) = I:    % I is the index of the blank space in the board.    % Throws invalid_board iff there is no blank.    %:- func space(board) = int.     % move(Move, !Board):    % Move the blank space in a board in the given direction.    % Fails if this is an invalid move to make.    %:- pred move(move::in, board::in, board::out) is semidet. :- implementation.:- import_module bt_array, int, list, string.:- use_module array, exception. :- type board == bt_array(int). init(B) :- from_list(0, (1 .. 15) ++ [0], B). print(B, !IO) :-    Tile = (func(N) = ( if N = 0 then s("  ") else s(string.format("%2d", [i(N)])) )),    io.format("\|----+----+----+----|| %s | %s | %s | %s || %s | %s | %s | %s || %s | %s | %s | %s || %s | %s | %s | %s ||----+----+----+----|",        map(Tile, to_list(B)), !IO). shuffled(!:B, !RS) :-    init(!:B),    some [!A] (        array.from_list(to_list(!.B), !:A),        array.random_permutation(!A, !RS),        from_list(0, array.to_list(!.A), !:B)    ). space(Board) = I :- space(0, Board, I). :- pred space(int::in, board::in, int::out) is det.space(N, Board, I) :-    ( if semidet_lookup(Board, N, X) then        ( if X = 0 then            N = I        else            space(N + 1, Board, I)        )    else        exception.throw(invalid_board(Board))    ). :- pred swap(int::in, int::in, board::in, board::out) is det.swap(I, J, !B) :-    X = !.B ^ elem(I),    Y = !.B ^ elem(J),    !B ^ elem(I) := Y,    !B ^ elem(J) := X. move(M, !B) :- move(space(!.B), M, !B). :- pred move(int::in, move::in, board::in, board::out) is semidet.move(I, up, !B) :-    I >= 4,    swap(I, I - 4, !B).move(I, down, !B) :-    I < 12,    swap(I, I + 4, !B).move(I, left, !B) :-    not (I = 0 ; I = 4 ; I = 8 ; I = 12),    swap(I, I - 1, !B).move(I, right, !B) :-    not (I = 3 ; I = 7 ; I = 11 ; I = 15),    swap(I, I + 1, !B).`

As used:

`:- module play_fifteen.:- interface.:- import_module io.:- pred main(io::di, io::uo) is det.:- implementation.:- import_module string.:- use_module random, fifteen, exception. main(!IO) :-    seed(Seed, !IO),    random.init(Seed, RS),    fifteen.shuffled(Board, RS, _),    fifteen.print(Board, !IO),    play(Board, !IO). :- type input_command    --->    up ; down ; left ; right    ;       quit. :- type command    --->    move(fifteen.move)    ;       quit. :- pred input(input_command::in, command::out) is det.input(up, move(fifteen.up)).input(down, move(fifteen.down)).input(left, move(fifteen.left)).input(right, move(fifteen.right)).input(quit, quit). :- pred command(string::in, command::out) is semidet.command(String, C) :-    Term = chomp(String) ++ ".",    io.read_from_string("play_fifteen", Term, length(Term),        ok(I), io.posn(0, 0, 0), _),    input(I, C). :- pred play(fifteen.board::in, io::di, io::uo) is det.play(!.B, !IO) :-    io.write_string("Move? ", !IO),    io.read_line_as_string(Res, !IO),    (        Res = eof    ;        Res = error(_),        exception.throw(Res)    ;        Res = ok(String),        ( if command(String, Command) then            (                Command = quit,                io.write_string("Bye :(\n", !IO)            ;                Command = move(Move),                ( if fifteen.move(Move, !B) then                    ( if fifteen.init(!.B) then                        fifteen.print(!.B, !IO),                        io.write_string("You win!\n", !IO)                    else                        fifteen.print(!.B, !IO),                        play(!.B, !IO)                    )                else                    io.write_string("Invalid move.\n", !IO),                    play(!.B, !IO)                )            )        else            io.write_string("I didn't understand that.\n", !IO),            play(!.B, !IO)        )    ). :- pragma foreign_decl("C", "#include <time.h>").:- pred seed(int::out, io::di, io::uo) is det.:- pragma foreign_proc("C",    seed(Seed::out, _IO0::di, _IO::uo),    [promise_pure, will_not_call_mercury],"    Seed = time(NULL);").`

## MUMPS

```15Game ;
; setting the layout
f i=1:1:15 s box(i)=16-i
; no number for box 16
s box(16)=""
f  {
; initialise i for incrementation
s i=0
; write out the 4-by-4 box
f row=1:1:4 {
w !?20
f column=1:1:4 {
s i=\$i(i)
w \$j(box(i),2)," "
}
}
r !!,"Enter number to move (q to quit): ",number
q:number="q"
f i=1:1:16 q:box(i)=""
if box(i)="" {
if i>4,number=box(i-4) {
s box(i)=number,box(i-4)=""
w !!
}
elseif i>1,i'=5,i'=9,i'=13,number=box(i-1) {
s box(i)=number,box(i-1)=""
w !!
}
elseif i<16,i'=4,i'=8,i'=12,number=box(i+1) {
s box(i)=number,box(i+1)=""
w !!
}
elseif i<13,number=box(i+4) {
s box(i)=number,box(i+4)=""
w !!
}
else {
w !,"You have to enter a number either above, below, left or right of the empty space."
}
}
}
q
```

## Nim

`import random, terminal type  Tile = uint8  Board = array[16, Tile] type  Operation = enum    opInvalid    opUp    opDown    opLeft    opRight    opQuit    opRestart func charToOperation(c: char): Operation =  case c  of 'w', 'W': opUp  of 'a', 'A': opLeft  of 's', 'S': opDown  of 'd', 'D': opRight  of 'q', 'Q': opQuit  of 'r', 'R': opRestart  else: opInvalid proc getKey(): Operation =  var c = getch()  if c == '\e':    c = getch()    if c == '[':      case getch()      of 'A': opUp      of 'D': opLeft      of 'B': opDown      of 'C': opRight      else: opInvalid    else: charToOperation c  else: charToOperation c func isSolved(board: Board): bool =  for i in 0..<board.high:    if i != board[i].int - 1:      return false  true func findTile(b: Board, n: Tile): int =  for i in 0..b.high:    if b[i] == n:      return i func canSwap(a, b: int): bool =  let dist = a - b  dist == 4 or dist == -4 or    (dist == 1 and a mod 4 != 0) or    (dist == -1 and b mod 4 != 0) func pad(i: Tile): string =  if i == 0:    "│  "  elif i < 10:    "│ " & \$i  else:    "│" & \$i proc draw(b: Board) =  echo "┌──┬──┬──┬──┐\n",     b[0].pad, b[1].pad, b[2].pad, b[3].pad,     "│\n├──┼──┼──┼──┤\n",     b[4].pad, b[5].pad, b[6].pad, b[7].pad,     "│\n├──┼──┼──┼──┤\n",     b[8].pad, b[9].pad, b[10].pad, b[11].pad,     "│\n├──┼──┼──┼──┤\n",     b[12].pad, b[13].pad, b[14].pad, b[15].pad,     "│\n└──┴──┴──┴──┘" proc clearScreen ()=  for i in 1..10:    eraseLine()    cursorUp() func calcPosMove(b: var Board; o: Operation; ): int =  var posEmpty = b.findTile 0  case o  of opUp:    return posEmpty + 4  of opDown:  return posEmpty - 4  of opLeft:  return posEmpty + 1  of opRight: return posEmpty - 1  else: return -1 proc moveTile (b : var Board, op : Operation) : bool =  let posMove = b.calcPosMove op  let posEmpty = b.findTile 0  if posMove < 16 and posMove >= 0 and canSwap(posEmpty, posMove):    swap b[posEmpty], b[posMove]    return true  return false proc shuffleBoard ( b: var Board, nSteps : int = 2000 ) =  var opMove = @[opUp, opLeft, opDown, opRight]  for i in 0 ..< nSteps:    let op = opMove[rand(3)]    discard b.moveTile op proc generateBoard: Board =  for i in 0..15:    if i == 15 :      result[i] = 0    else:      result[i] = (i + 1).Tile  shuffleBoard result when isMainModule:  randomize()  var    board = generateBoard()    empty = board.findTile 0   block gameLoop:    while not isSolved board:      # draw      draw board      echo "Press arrow keys or WASD to move, R to Restart, Q to Quit"       # handle input      while true:        let op = getKey()        case op        of opRestart:          board = generateBoard()          empty = board.findTile 0          break        of opQuit: break gameLoop        of opInvalid: continue        else:          if board.moveTile op:            empty = board.findTile 0            break       clearScreen()     draw board    echo "You win!"`
Output:
```┌──┬──┬──┬──┐
│12│11│15│ 2│
├──┼──┼──┼──┤
│ 6│14│ 3│ 7│
├──┼──┼──┼──┤
│ 5│  │ 9│10│
├──┼──┼──┼──┤
│ 1│ 4│ 8│13│
└──┴──┴──┴──┘
Press arrow keys or WASD to move, R to Restart, Q to Quit```

## OCaml

`module Puzzle =struct  type t = int array  let make () =    [| 15; (* 0: the empty space *)        0;  1;  2;  3;        4;  5;  6;  7;        8;  9; 10; 11;       12; 13; 14;  |]   let move p n =    let hole, i = p.(0), p.(n) in    p.(0) <- i;    p.(n) <- hole   let print p =    let out = Array.make 16 "   " in    for i = 1 to 15 do      out.(p.(i)) <- Printf.sprintf " %2d" i    done;    for i = 0 to 15 do      if (i mod 4) = 0 then print_newline ();      print_string out.(i);    done   let shuffle p n =    for i = 1 to n do      move p (1 + Random.int 15)    doneend let play () =  let p = Puzzle.make () in  Puzzle.shuffle p 20;  while true do    Puzzle.print p;    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.

Output:
```# play ();;

8 11  7 13
3  6    15
9 10 12  4
1  5 14  2 > 6

8 11  7 13
3     6 15
9 10 12  4
1  5 14  2 > 11

8     7 13
3 11  6 15
9 10 12  4
1  5 14  2 > 8

8  7 13
3 11  6 15
9 10 12  4
1  5 14  2 > 3

3  8  7 13
11  6 15
9 10 12  4
1  5 14  2 > 11

3  8  7 13
11     6 15
9 10 12  4
1  5 14  2 > 8

3     7 13
11  8  6 15
9 10 12  4
1  5 14  2 > 3

3  7 13
11  8  6 15
9 10 12  4
1  5 14  2 >
```

## 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.

` program fifteen;{\$mode objfpc}{\$modeswitch advancedrecords}{\$coperators on}uses  SysUtils, crt;type  TPuzzle = record  private  const    ROW_COUNT  = 4;    COL_COUNT  = 4;    CELL_COUNT = ROW_COUNT * COL_COUNT;    RAND_RANGE = 101;  type    TTile          = 0..Pred(CELL_COUNT);    TAdjacentCell  = (acLeft, acTop, acRight, acBottom);    TPossibleMoves = set of TTile;    TCellAdjacency = set of TAdjacentCell;    TBoard         = array[0..Pred(CELL_COUNT)] of TTile;  class var    HBar: string;  var    FBoard: TBoard;    FZeroPos,    FMoveCount: Integer;    FZeroAdjacency: TCellAdjacency;    FPossibleMoves: TPossibleMoves;    FSolved: Boolean;    procedure DoMove(aTile: TTile);    procedure CheckPossibleMoves;    procedure PrintBoard;    procedure PrintPossibleMoves;    procedure TestSolved;    procedure GenerateBoard;    class constructor Init;  public    procedure New;    function  UserMoved: Boolean;    property  MoveCount: Integer read FMoveCount;    property  Solved: Boolean read FSolved;  end; procedure TPuzzle.DoMove(aTile: TTile);var  Pos: Integer = -1;  Adj: TAdjacentCell;begin  for Adj in FZeroAdjacency do    begin      case Adj of        acLeft:   Pos := Pred(FZeroPos);        acTop:    Pos := FZeroPos - COL_COUNT;        acRight:  Pos := Succ(FZeroPos);        acBottom: Pos := FZeroPos + COL_COUNT;      end;      if FBoard[Pos] = aTile then        break;    end;  FBoard[FZeroPos] := aTile;  FZeroPos := Pos;  FBoard[Pos] := 0;end; procedure TPuzzle.CheckPossibleMoves;var  Row, Col: Integer;begin  Row := FZeroPos div COL_COUNT;  Col := FZeroPos mod COL_COUNT;  FPossibleMoves := [];  FZeroAdjacency := [];  if Row > 0 then    begin      FPossibleMoves += [FBoard[FZeroPos - COL_COUNT]];      FZeroAdjacency += [acTop];    end;  if Row < Pred(ROW_COUNT) then    begin      FPossibleMoves += [FBoard[FZeroPos + COL_COUNT]];      FZeroAdjacency += [acBottom];    end;  if Col > 0 then    begin      FPossibleMoves += [FBoard[Pred(FZeroPos)]];      FZeroAdjacency += [acLeft];    end;  if Col < Pred(COL_COUNT) then    begin      FPossibleMoves += [FBoard[Succ(FZeroPos)]];      FZeroAdjacency += [acRight];    end;end; procedure TPuzzle.PrintBoard;const  Space = ' ';  VBar  = '|';  VBar1 = '| ';  VBar2 = '|  ';  VBar3 = '|    ';var  I, J, Pos, Tile: Integer;  Row: string;begin  ClrScr;  Pos := 0;  WriteLn(HBar);  for I := 1 to ROW_COUNT do    begin      Row := '';      for J := 1 to COL_COUNT do        begin          Tile := Integer(FBoard[Pos]);          case Tile of            0:    Row += VBar3;            1..9: Row += VBar2 + Tile.ToString + Space;          else            Row += VBar1 + Tile.ToString + Space;          end;          Inc(Pos);        end;      WriteLn(Row + VBar);      WriteLn(HBar);    end;  if not Solved then    PrintPossibleMoves;end; procedure TPuzzle.PrintPossibleMoves;var  pm: TTile;  spm: string = '';begin  for pm in FPossibleMoves do    spm += Integer(pm).ToString + ' ';  WriteLn('possible moves: ', spm);end; procedure TPuzzle.TestSolved;  function IsSolved: Boolean;  var    I: Integer;  begin    for I := 0 to CELL_COUNT - 3 do      if FBoard[I] <> Pred(FBoard[Succ(I)]) then        exit(False);    Result := True;  end;begin  FSolved := IsSolved;  if not Solved then    CheckPossibleMoves;end; procedure TPuzzle.GenerateBoard;var  I, CurrMove, SelMove: Integer;  Tile: TTile;begin  FZeroPos := Pred(CELL_COUNT);  FBoard[FZeroPos] := 0;  for I := 0 to CELL_COUNT - 2 do    FBoard[I] := Succ(I);  for I := 1 to Random(RAND_RANGE) do    begin      CheckPossibleMoves;      SelMove := 0;      for Tile in FPossibleMoves do        Inc(SelMove);      SelMove := Random(SelMove);      CurrMove := 0;      for Tile in FPossibleMoves do        begin          if CurrMove = SelMove then            begin              DoMove(Tile);              break;            end;          Inc(CurrMove);        end;    end;end; class constructor TPuzzle.Init;var  I: Integer;begin  HBar := '';  for I := 1 to COL_COUNT do    HBar += '+----';  HBar += '+';end; procedure TPuzzle.New;begin  FSolved := False;  FMoveCount := 0;  GenerateBoard;  CheckPossibleMoves;  PrintBoard;end; function TPuzzle.UserMoved: Boolean;const  Sorry          = 'sorry, ';  InvalidInput   = ' is invalid input';  ImpossibleMove = ' is impossible move';var  UserInput: string;  Tile: Integer = 0;begin  ReadLn(UserInput);  case LowerCase(UserInput) of    'c', 'cancel': exit(False);  end;  Result := True;  if not Tile.TryParse(UserInput, Tile) then    begin      WriteLn(Sorry, UserInput, InvalidInput);      exit;    end;  if not (Tile in [1..Pred(CELL_COUNT)]) then    begin      WriteLn(Sorry, Tile, InvalidInput);      exit;    end;  if not (Tile in FPossibleMoves) then    begin      WriteLn(Sorry, Tile, ImpossibleMove);      PrintPossibleMoves;      exit;    end;  DoMove(Tile);  Inc(FMoveCount);  TestSolved;  PrintBoard;end; procedure PrintStart;begin  ClrScr;  WriteLn('Fifteen puzzle start:');  WriteLn('  enter a tile number and press <enter> to move' );  WriteLn('  enter Cancel(C) and press <enter> to exit' );  Window(10, 4, 58, 21);end; procedure Terminate;begin  ClrScr;  Window(1, 1, 80, 25);  ClrScr;  WriteLn('Fifteen puzzle exit.');  Halt;end; function UserWantContinue(aMoveCount: Integer): Boolean;var  UserInput: string;begin  WriteLn('Congratulations! Puzzle solved in ', aMoveCount, ' moves.');  WriteLn('Play again(Yes(Y)/<any button>)?');  ReadLn(UserInput);  case LowerCase(UserInput) of    'y', 'yes': exit(True);  end;  Result := False;end; procedure Run;var  Puzzle: TPuzzle;begin  Randomize;  PrintStart;  repeat    Puzzle.New;    while not Puzzle.Solved do      if not Puzzle.UserMoved then        Terminate;    if not UserWantContinue(Puzzle.MoveCount) then      Terminate;  until False;end; begin  Run;end. `

## Perl

### Tk version

Library: Tk
Library: Perl/Tk

This Tk 15 puzzle implementation also shows the solvability of the current puzzle and the relative difficulty of it. 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 perlmonks

`  use strict;use warnings; use Getopt::Long;use List::Util 1.29 qw(shuffle pairmap first all);use Tk;    # 5 options                                 1 label textmy (\$verbose,@fixed,\$nocolor,\$charsize,\$extreme,\$solvability); unless (GetOptions (                     'verbose!' => \\$verbose,                     'tiles|positions=i{16}' => \@fixed,                     'nocolor' => \\$nocolor,                     'charsize|size|c|s=i' => \\$charsize,                     'extreme|x|perl' => \\$extreme,                    )        ) { die "invalid arguments!";} @fixed = &check_req_pos(@fixed) if @fixed; my \$mw = Tk::MainWindow->new(-bg=>'black',-title=>'Giuoco del 15'); if (\$nocolor){ \$mw->optionAdd( '*Button.background',   'ivory' );} \$mw->optionAdd('*Button.font', 'Courier '.(\$charsize or 16).' bold' );\$mw->bind('<Control-s>', sub{#&init_board;                             &shuffle_board}); my \$top_frame = \$mw->Frame( -borderwidth => 2, -relief => 'groove',                           )->pack(-expand => 1, -fill => 'both'); \$top_frame->Label( -textvariable=>\\$solvability,                  )->pack(-expand => 1, -fill => 'both'); my \$game_frame = \$mw->Frame(  -background=>'saddlebrown',                              -borderwidth => 10, -relief => 'groove',                            )->pack(-expand => 1, -fill => 'both'); # set victory conditions in pairs of coordinatesmy @vic_cond =  pairmap {       [\$a,\$b]    } qw(0 0 0 1 0 2 0 3         1 0 1 1 1 2 1 3         2 0 2 1 2 2 2 3         3 0 3 1 3 2 3 3); my \$board = []; my \$victorious = 0; &init_board; if ( \$extreme ){ &extreme_perl} &shuffle_board; MainLoop; ################################################################################sub init_board{  # tiles from 1 to 15  for (0..14){     \$\$board[\$_]={          btn=>\$game_frame->Button(                            -text => \$_+1,                            -relief => 'raised',                            -borderwidth => 3,                            -height => 2,                            -width =>  4,                                  -background=>\$nocolor?'ivory':'gold1',                                  -activebackground => \$nocolor?'ivory':'gold1',                                  -foreground=> \$nocolor?'black':'DarkRed',                                  -activeforeground=>\$nocolor?'black':'DarkRed'          ),          name => \$_+1,     # x and y set by shuffle_board     };     if ((\$_+1) =~ /^(2|4|5|7|10|12|13|15)\$/ and !\$nocolor){         \$\$board[\$_]{btn}->configure(                                  -background=>'DarkRed',                                  -activebackground => 'DarkRed',                                  -foreground=> 'gold1',                                  -activeforeground=>'gold1'         );     }   }   # empty tile   \$\$board[15]={          btn=>\$game_frame->Button(                            -relief => 'sunken',                            -borderwidth => 3,                            -background => 'lavender',                            -height => 2,                            -width =>  4,          ),          name => 16,      # x and y set by shuffle_board     };}################################################################################sub shuffle_board{    if (\$victorious){        \$victorious=0;        &init_board;    }    if (@fixed){          my \$index = 0;           foreach my \$tile(@\$board[@fixed]){                  my \$xy = \$vic_cond[\$index];                  (\$\$tile{x},\$\$tile{y}) = @\$xy;                  \$\$tile{btn}->grid(-row=>\$\$xy[0], -column=> \$\$xy[1]);                  \$\$tile{btn}->configure(-command =>[\&move,\$\$xy[0],\$\$xy[1]]);                  \$index++;          }          undef @fixed;    }    else{        my @valid = shuffle (0..15);        foreach my \$tile ( @\$board ){            my \$xy = \$vic_cond[shift @valid];            (\$\$tile{x},\$\$tile{y}) = @\$xy;            \$\$tile{btn}->grid(-row=>\$\$xy[0], -column=> \$\$xy[1]);            \$\$tile{btn}->configure(-command => [ \&move, \$\$xy[0], \$\$xy[1] ]);        }    }    my @appear =  map {\$_->{name}==16?'X':\$_->{name}}                  sort{\$\$a{x}<=>\$\$b{x}||\$\$a{y}<=>\$\$b{y}}@\$board;    print "\n".('-' x 57)."\n".          "Appearence of the board:\n[@appear]\n".          ('-' x 57)."\n".          "current\tfollowers\t               less than current\n".          ('-' x 57)."\n" if \$verbose;    # remove the, from now on inutile, 'X' for the empty space    @appear = grep{\$_ ne 'X'} @appear;    my \$permutation;    foreach my \$num (0..\$#appear){        last if \$num == \$#appear;         my \$perm;          \$perm += grep {\$_ < \$appear[\$num]} @appear[\$num+1..\$#appear];          if (\$verbose){            print "[\$appear[\$num]]\t@appear[\$num+1..\$#appear]".            (" " x (37 - length "@appear[\$num+1..\$#appear]")).            "\t   \$perm ".(\$num == \$#appear  - 1 ? '=' : '+')."\n";          }          \$permutation+=\$perm;    }    print +(' ' x 50)."----\n" if \$verbose;    if (\$permutation % 2){        print "Impossible game with odd permutations!".(' ' x 13).              "\$permutation\n"if \$verbose;        \$solvability = "Impossible game with odd permutations [\$permutation]\n".                        "(ctrl-s to shuffle)".                        ((\$verbose or \$extreme) ? '' :                           " run with --verbose to see more info");        return;    }    # 105 is the max permutation    my \$diff =  \$permutation == 0 ? 'SOLVED' :                \$permutation < 35 ? 'EASY  ' :                \$permutation < 70 ? 'MEDIUM' : 'HARD  ';    print "\$diff game with even permutations".(' ' x 17).          "\$permutation\n" if \$verbose;    \$solvability = "\$diff game with permutation parity of [\$permutation]\n".                    "(ctrl-s to shuffle)";}################################################################################sub move{    # original x and y    my (\$ox, \$oy) = @_;    my \$self = first{\$_->{x} == \$ox and \$_->{y} == \$oy} @\$board;    return if \$\$self{name}==16;    # check if one in n,s,e,o is the empty one    my \$empty = first {\$_->{name} == 16 and                          ( (\$_->{x}==\$ox-1 and \$_->{y}==\$oy) or                            (\$_->{x}==\$ox+1 and \$_->{y}==\$oy) or                            (\$_->{x}==\$ox and \$_->{y}==\$oy-1) or                            (\$_->{x}==\$ox and \$_->{y}==\$oy+1)                           )                      } @\$board;    return unless \$empty;    # empty x and y    my (\$ex,\$ey) = (\$\$empty{x},\$\$empty{y});    # reconfigure emtpy tile    \$\$empty{btn}->grid(-row => \$ox, -column => \$oy);    \$\$empty{x}=\$ox;    \$\$empty{y}=\$oy;    # reconfigure pressed tile    \$\$self{btn}->grid(-row => \$ex, -column => \$ey);    \$\$self{btn}->configure(-command => [ \&move, \$ex, \$ey ]);    \$\$self{x}=\$ex;    \$\$self{y}=\$ey;    # check for victory if the empty one is at the bottom rigth tile (3,3)    &check_win if \$\$empty{x} == 3 and \$\$empty{y} == 3;}################################################################################sub check_win{     foreach my \$pos (0..\$#\$board){        return unless ( \$\$board[\$pos]->{'x'} == \$vic_cond[\$pos]->[0] and                        \$\$board[\$pos]->{'y'} == \$vic_cond[\$pos]->[1]);     }     # victory!     \$victorious = 1;     my @text =  ('Dis','ci','pu','lus','15th','','','at',                  'P','e','r','l','M','o','n','ks*');     foreach my \$tile(@\$board){            \$\$tile{btn}->configure( -text=> shift @text,                                    -command=>sub{return});            \$mw->update;            sleep 1;     }}################################################################################sub check_req_pos{    my @wanted = @_;    # fix @wanted: seems GetOptions does not die if more elements are passed    @wanted = @wanted[0..15];    my @check = (1..16);    unless ( all {\$_ == shift @check} sort {\$a<=>\$b} @wanted ){        die "tiles must be from 1 to 16 (empty tile)\nyou passed [@wanted]\n";    }    return map {\$_-1} @wanted;}################################################################################sub extreme_perl {  \$verbose = 0;  \$mw->optionAdd('*font', 'Courier 20 bold');  my @extreme = (    'if \$0',                               #1    "\\$_=\n()=\n\"foo\"=~/o/g",            #2    "use warnings;\n\\$^W ?\nint((length\n'Discipulus')/3)\n:'15'",   #3    "length \\$1\nif \\$^X=~\n\/(?:\\W)(\\w*)\n(?:\\.exe)\\$\/", #4    "use Config;\n\\$Config{baserev}",                   #5.    "(split '',\nvec('JAPH'\n,1,8))[0]",       #6    "scalar map\n{ord(\\$_)=~/1/g}\nqw(p e r l)", #7    "\\$_ = () =\n'J A P H'\n=~\/\\b\/g",   # 8    "eval join '+',\nsplit '',\n(substr\n'12345',3,2)",  #9    'printf \'%b\',2',                     #10    "int(((1+sqrt(5))\n/ 2)** 7 /\nsqrt(5)+0.5)-2",    #11    "split '',\nunpack('V',\n01234567))\n[6,4]",  # 12    'J','A','P','H'                               # 13..16  );  foreach (0..15){      \$\$board[\$_]{btn}->configure(-text=> \$extreme[\$_],                                 -height => 8,                                  -width =>  16, ) if \$extreme[\$_];   }  @fixed = qw(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15);  \$mw->after(5000,\&shuffle_board);#}   `

### 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 perlmonks but here a little modification was inserted to prevent wrong numbers to make the board messy.

` use strict; use warnings; use List::Util qw(shuffle first);my @tbl = ([1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]);my \$e = [3,3];for (1..\$ARGV[0]||1000) {  my \$new = (shuffle &ad(\$e))[0];  \$tbl[\$e->[0]][\$e->[1]] = \$tbl[\$new->[0]][\$new->[1]];  \$tbl[\$new->[0]]->[\$new->[1]] = 16;  \$e = [\$new->[0],\$new->[1]];}while(1){  print +(join ' ',map{\$_==16?'  ':sprintf '%02s',\$_}@{\$tbl[\$_]}),"\n" for 0..3;  my \$m = <STDIN>;  chomp \$m;    die "Enter a number to move!" unless \$m;  if (\$m > 15){	warn "\$m not in the board! Enter a tile from 1 to 15\n";	next;  }  my \$tile=first{\$tbl[\$\$_[0]]->[\$\$_[1]]==\$m}map{[\$_,0],[\$_,1],[\$_,2],[\$_,3]}0..3;  my \$new=first{\$tbl[\$\$_[0]]->[\$\$_[1]]==16}&ad(grep{\$tbl[\$\$_[0]]->[\$\$_[1]]==\$m}          map {[\$_,0],[\$_,1],[\$_,2],[\$_,3]}0..3);  if (\$new){\$tbl[\$\$new[0]][\$\$new[1]]=\$m;\$tbl[\$\$tile[0]][\$\$tile[1]]=16;}  system (\$^O eq 'MSWin32' ? 'cls' : 'clear');}sub ad{      my \$e = shift; grep {\$_->[0]<4 && \$_->[1]<4 && \$_->[0]>-1 && \$_->[1]>-1}      [\$\$e[0]-1,\$\$e[1]],[\$\$e[0]+1,\$\$e[1]],[\$\$e[0],\$\$e[1]-1],[\$\$e[0],\$\$e[1]+1]} `

## Phix

Kept simple. Obviously, increase the 5 random moves for more of a challenge.

```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")
```
Output:
```  1  2  3  4
5     6  8
9 10  7 11
13 14 15 12

1  2  3  4
5  6     8
9 10  7 11
13 14 15 12

1  2  3  4
5  6  7  8
9 10    11
13 14 15 12

1  2  3  4
5  6  7  8
9 10 11
13 14 15 12

1  2  3  4
5  6  7  8
9 10 11 12
13 14 15

solved!
```

## PHP

OOP and MVC design pattern has been used to facilitate any improvements, e.g. tiles with graphics instead of just numbers.

`<?php// Puzzle 15 Game - Rosseta Code - PHP 7 as the server-side script language. // This program DOES NOT use cookies. session_start([  "use_only_cookies" => 0,  "use_cookies" => 0,  "use_trans_sid" => 1,]); class Location{  protected \$column, \$row;   function __construct(\$column, \$row){    \$this->column = \$column;    \$this->row = \$row;  }  function create_neighbor(\$direction){    \$dx = 0; \$dy = 0;    switch (\$direction){      case 0: case 'left':  \$dx = -1; break;      case 1: case 'right': \$dx = +1; break;      case 2: case 'up':    \$dy = -1; break;      case 3: case 'down':  \$dy = +1; break;    }    return new Location(\$this->column + \$dx, \$this->row + \$dy);  }  function equals(\$that){    return \$this->column == \$that->column && \$this->row == \$that->row;  }  function is_inside_rectangle(\$left, \$top, \$right, \$bottom){    return \$left <= \$this->column && \$this->column <= \$right        && \$top <= \$this->row && \$this->row <= \$bottom;  }  function is_nearest_neighbor(\$that){    \$s = abs(\$this->column - \$that->column) + abs(\$this->row - \$that->row);    return \$s == 1;  }} class Tile{  protected \$index;  protected \$content;  protected \$target_location;  protected \$current_location;   function __construct(\$index, \$content, \$row, \$column){    \$this->index = \$index;    \$this->content = \$content;    \$this->target_location = new Location(\$row, \$column);    \$this->current_location = \$this->target_location;  }  function get_content(){    return \$this->content;  }  function get_index(){    return \$this->index;  }  function get_location(){    return \$this->current_location;  }  function is_completed(){    return \$this->current_location->equals(\$this->target_location);  }  function is_empty(){    return \$this->content == NULL;  }  function is_nearest_neighbor(\$that){    \$a = \$this->current_location;    \$b = \$that->current_location;    return \$a->is_nearest_neighbor(\$b);  }  function swap_locations(\$that){    \$a = \$this->current_location;    \$b = \$that->current_location;    \$this->current_location = \$b;    \$that->current_location = \$a;  }} class Model{  protected \$N;  protected \$M;  protected \$tiles;   function __construct(\$N, \$M){    \$this->N = \$N;    \$this->M = \$M;    \$this->tiles[0] = new Tile(0, NULL, \$N, \$M);    for (\$k = 1; \$k < \$N * \$M; \$k++ ){      \$i = 1 + intdiv(\$k - 1, \$M);      \$j = 1 + (\$k - 1) % \$M;      \$this->tiles[\$k] = new Tile(\$k, (string)\$k, \$i, \$j);    }    \$number_of_shuffles = 1000;    \$i = 0;    while (\$i < \$number_of_shuffles)      if (\$this->move_in_direction(random_int(0, 3)))        \$i++;  }  function get_N(){    return \$this->N;  }  function get_M(){    return \$this->M;  }  function get_tile_by_index(\$index){    return \$this->tiles[\$index];  }  function get_tile_at_location(\$location){    foreach(\$this->tiles as \$tile)      if (\$location->equals(\$tile->get_location()))        return \$tile;    return NULL;  }  function is_completed(){    foreach(\$this->tiles as \$tile)      if (!\$tile->is_completed())        return FALSE;    return TRUE;  }  function move(\$tile){    if (\$tile != NULL)      foreach(\$this->tiles as \$target){        if (\$target->is_empty() && \$target->is_nearest_neighbor(\$tile)){          \$tile->swap_locations(\$target);          break;        }      }  }  function move_in_direction(\$direction){    foreach(\$this->tiles as \$tile)      if (\$tile->is_empty())        break;       \$location = \$tile->get_location()->create_neighbor(\$direction);    if (\$location->is_inside_rectangle(0, 0, \$this->M, \$this->N)){      \$tile = \$this->get_tile_at_location(\$location);      \$this->move(\$tile);      return TRUE;    }    return FALSE;  }} class View{  protected \$model;   function __construct(\$model){      \$this->model = \$model;  }  function show(){    \$N = \$this->model->get_N();    \$M = \$this->model->get_M();    echo "<form>";    for (\$i = 1; \$i <= \$N; \$i++){      for (\$j = 1; \$j <= \$M; \$j++){        \$tile = \$this->model->get_tile_at_location(new Location(\$i, \$j));        \$content = \$tile->get_content();        if (\$content != NULL)          echo "<span class='puzzle'>"          .    "<input type='submit' class='puzzle' name='index'"          .    "value='\$content'>"          .    "</span>";        else          echo "<span class='puzzle'></span>";    }      echo "<br>";    }    echo "</form>";    if (\$this->model->is_completed()){      echo "<p class='end-game'>";      echo "You win!";      echo "</p>";    }  }} class Controller{  protected \$model;  protected \$view;   function __construct(\$model, \$view){    \$this->model = \$model;    \$this->view = \$view;  }  function run(){    if (isset(\$_GET['index'])){      \$index = \$_GET['index'];      \$this->model->move(\$this->model->get_tile_by_index(\$index));    }    \$this->view->show();  }}?> <!DOCTYPE html><html lang="en"><meta charset="UTF-8"><head>  <title>15 puzzle game</title>  <style>    .puzzle{width: 4ch; display: inline-block; margin: 0; padding: 0.25ch;}    span.puzzle{padding: 0.1ch;}    .end-game{font-size: 400%; color: red;}  </style></head><body>  <p><?php    if (!isset(\$_SESSION['model'])){      \$width = 4; \$height = 4;      \$model = new Model(\$width, \$height);    }    else      \$model = unserialize(\$_SESSION['model']);    \$view = new View(\$model);    \$controller = new Controller(\$model, \$view);    \$controller->run();    \$_SESSION['model'] = serialize(\$model);  ?></p></body></html>`

## Powershell

Had a go at this in PowerShell. The board locks when you complete the puzzle. 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.

` #15 Puzzle Game\$Script:Neighbours = @{    "1" = @("2","5")    "2" = @("1","3","6")    "3" = @("2","4","7")    "4" = @("3","8")    "5" = @("1","6","9")    "6" = @("2","5","7","10")    "7" = @("3","6","8","11")    "8" = @("4","7","12")    "9" = @("5","10","13")    "10" = @("6","9","11","14")    "11" = @("7","10","12","15")    "12" = @("8","11","0")    "13" = @("9","14")    "14" = @("10","13","15")    "15" = @("11","14","0")    "0" = @("12","15")}\$script:blank = ''#region XAML window definition\$xaml = @'<Window   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"   MinWidth="200"   Width ="333.333"   Title="15 Game"   Topmost="True" Height="398.001" VerticalAlignment="Center" HorizontalAlignment="Center">    <Grid HorizontalAlignment="Center" Height="285" Margin="0" VerticalAlignment="Center" Width="300">        <Grid.ColumnDefinitions>            <ColumnDefinition/>            <ColumnDefinition/>            <ColumnDefinition/>            <ColumnDefinition/>        </Grid.ColumnDefinitions>        <Grid.RowDefinitions>            <RowDefinition/>            <RowDefinition/>            <RowDefinition/>            <RowDefinition/>        </Grid.RowDefinitions>        <Button x:Name="B_1" Content="01" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24"/>        <Button x:Name="B_2" Content="02" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="1"/>        <Button x:Name="B_3" Content="03" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="2"/>        <Button x:Name="B_4" Content="04" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="3"/>        <Button x:Name="B_5" Content="05" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Row="1"/>        <Button x:Name="B_6" Content="06" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="1" Grid.Row="1"/>        <Button x:Name="B_7" Content="07" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="2" Grid.Row="1"/>        <Button x:Name="B_8" Content="08" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="3" Grid.Row="1"/>        <Button x:Name="B_9" Content="09" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Margin="0,71,0,0" Grid.Row="1" Grid.RowSpan="2"/>        <Button x:Name="B_10" Content="10" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="1" Grid.Row="1" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_11" Content="11" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="2" Grid.Row="1" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_12" Content="12" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="3" Grid.Row="1" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_13" Content="13" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Margin="0,71,0,0" Grid.Row="2" Grid.RowSpan="2"/>        <Button x:Name="B_14" Content="14" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="1" Grid.Row="2" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_15" Content="15" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" Height="72" FontSize="24" Grid.Column="2" Grid.Row="2" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_0" Content="00" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Height="72" FontSize="24" Grid.Column="3" Grid.Row="2" Margin="0,71,0,0" Grid.RowSpan="2"/>        <Button x:Name="B_Jumble" Grid.ColumnSpan="2" Content="Jumble Tiles" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="0,81,0,-33" Grid.Row="3" VerticalAlignment="Top" Width="150"/>    </Grid></Window>'@#endregion #region Code Behindfunction Convert-XAMLtoWindow{    param    (        [Parameter(Mandatory=\$true)]        [string]        \$XAML    )     Add-Type -AssemblyName PresentationFramework     \$reader = [XML.XMLReader]::Create([IO.StringReader]\$XAML)    \$result = [Windows.Markup.XAMLReader]::Load(\$reader)    \$reader.Close()    \$reader = [XML.XMLReader]::Create([IO.StringReader]\$XAML)    while (\$reader.Read())    {        \$name=\$reader.GetAttribute('Name')        if (!\$name) { \$name=\$reader.GetAttribute('x:Name') }        if(\$name)        {\$result | Add-Member NoteProperty -Name \$name -Value \$result.FindName(\$name) -Force}    }    \$reader.Close()    \$result}function Show-WPFWindow{    param    (        [Parameter(Mandatory=\$true)]        [Windows.Window]        \$Window    )     \$result = \$null    \$null = \$window.Dispatcher.InvokeAsync{        \$result = \$window.ShowDialog()        Set-Variable -Name result -Value \$result -Scope 1    }.Wait()    \$result}#endregion Code Behind #region Convert XAML to Window\$window = Convert-XAMLtoWindow -XAML \$xaml  #endregion #region Define Event Handlersfunction Test-Victory{    #Evaluate if all the labels are in the correct position    \$victory = \$true    foreach(\$num in 1..15){        if([int]\$window."b_\$num".Content -ne \$num){\$victory = \$false;break}    }    return(\$victory)}function Test-Move(\$Number){    #Number is a string of the pressed button number.    if(\$Script:Neighbours[\$Number] -contains \$script:blank){        return(\$true)    } else {        return(\$false)    }}Function Move-Tile(\$Number,\$Bypass){    if((!(Test-Victory)) -or \$Bypass){        if(Test-Move \$Number){            #Set the new window label            \$window."B_\$script:blank".content = \$window."B_\$Number".content            \$window."B_\$script:blank".background = \$window."B_\$Number".background            \$window."B_\$Number".background = "#FFDDDDDD"            #Set the new blank window label            \$window."B_\$Number".content = ''            #Enable the old blank tile            \$window."B_\$script:blank".isenabled = \$true            #disable the new blank tile            \$window."B_\$Number".isenabled = \$false            #set the new blank            \$script:blank = \$Number        }    }}function Move-TileRandom{    \$lastmove = "1"    for(\$i=0;\$i -lt 500;\$i++){        \$move = \$Script:Neighbours[\$script:blank] | Where-Object {\$_ -ne \$lastmove} | Get-Random        \$lastmove = \$move        Move-Tile \$move \$true    }}function Set-TileColour(\$Tile){    #I was curious about setting tiles to a checkerboard pattern at this stage. It's probably far better to just define it in the xaml    #Ignore the blank tile    if(\$Tile -ne 0){        #check if the row of the tile is odd or even        if((([math]::floor((\$Tile - 1)/4)) % 2 -eq 0)){            #check if the tile is odd or even            if(\$Tile % 2 -eq 0){                \$window."B_\$Tile".Background = "#FFFF7878"            } else {                \$window."B_\$Tile".Background = "#FF9696FF"            }        }else{            if(\$Tile % 2 -eq 0){                \$window."B_\$Tile".Background = "#FF9696FF"            } else {                \$window."B_\$Tile".Background = "#FFFF7878"            }        }    } else {        \$window.B_0.Background = "#FFDDDDDD"    }}\$window.B_1.add_Click{    \$n = "1"    move-tile \$n}\$window.B_2.add_Click{    \$n = "2"    move-tile \$n}\$window.B_3.add_Click{    \$n = "3"    move-tile \$n}\$window.B_4.add_Click{    \$n = "4"    move-tile \$n}\$window.B_5.add_Click{    \$n = "5"    move-tile \$n}\$window.B_6.add_Click{    \$n = "6"    move-tile \$n}\$window.B_7.add_Click{    \$n = "7"    move-tile \$n}\$window.B_8.add_Click{    \$n = "8"    move-tile \$n}\$window.B_9.add_Click{    \$n = "9"    move-tile \$n}\$window.B_10.add_Click{    \$n = "10"    move-tile \$n}\$window.B_11.add_Click{    \$n = "11"    move-tile \$n}\$window.B_12.add_Click{    \$n = "12"    move-tile \$n}\$window.B_13.add_Click{    \$n = "13"    move-tile \$n}\$window.B_14.add_Click{    \$n = "14"    move-tile \$n}\$window.B_15.add_Click{    \$n = "15"    move-tile \$n}\$window.B_0.add_Click{    \$n = "0"    move-tile \$n} \$window.B_Jumble.add_Click{    Move-TileRandom}#endregion Event Handlers(([math]::floor(("9" - 1)/4)) % 2 -eq 0)#region Manipulate Window Content#initial processing of tiles\$array = 0..15 | ForEach-Object {"{0:00}" -f \$_}0..15 | ForEach-Object {if(\$array[\$_] -ne '00'){\$window."B_\$_".content = \$array[\$_]} else {\$window."B_\$_".content = '';\$script:blank = "\$_";\$window."B_\$_".isenabled = \$false};Set-TileColour \$_}#Shove them around a bitMove-TileRandom#endregion# Show Window\$result = Show-WPFWindow -Window \$window `

## 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);color background_color = color(235, 231, 178);color piece_shadow_dark = color(206, 141, 0);color piece_shadow_light = color(255, 214, 126);int z, t, p, piece_number, row_length, piece_side_length; PuzzlePiece[] piece = new PuzzlePiece[number_of_grid_cells]; //  Number of puzzle pieces objects array void setup() {   size(400, 400);  // Window size width and height must be egual	background(200, 50, 0);  row_length = int(sqrt(number_of_grid_cells));  piece_side_length = width/row_length;  textSize(piece_side_length/2.7);  textAlign(CENTER);   PVector[] xy_values = new PVector[number_of_grid_cells]; //  Setting the x and y values for each cell on grid  for (int i = 0; i < number_of_grid_cells; i += row_length) { // Values are the top left pixel of the cell    for (int j = 0; j < row_length; j++) {      xy_values[z] = new PVector();      xy_values[z].x = j*piece_side_length;      xy_values[z].y = t*piece_side_length;       z++;    }    t++;  }    int[] place = new int[number_of_grid_cells]; // This array is  to help placing the pieces randomly and store values in piece objects array  for (int i = 0; i < number_of_grid_cells; i++) place[i] = 0;  piece_number = 0;   while (piece_number < number_of_grid_cells) {  // Placing pieces randomly in grid    p = int(random(0, number_of_grid_cells));    if (place[p] == 0) { // Once placed will be set to 1 to avoid designing again at this location      piece[piece_number] = new PuzzlePiece(piece_number, xy_values[p].x, xy_values[p].y); // Creating the piece objects array       place[p] = 1;      piece[piece_number].design(); // Design newly create piece object      piece_number++;    }  }} void draw() {    for (int i = 0; i < number_of_grid_cells; i++) {   // Search all piece object indexes and verify which one is mouse pressed in this loop    if (mousePressed == true && mouseX >= piece[i].xPosition() && mouseX <= piece[i].xPosition()+piece_side_length && mouseY >= piece[i].yPosition() && mouseY <= piece[i].yPosition()+piece_side_length && piece[i].pieceNumber() != 15) {        if (pieceMove(piece[number_of_grid_cells-1].xPosition(), piece[number_of_grid_cells-1].yPosition(), piece[i].xPosition(), piece[i].yPosition())) {        float temp_x = piece[number_of_grid_cells-1].xPosition(); // Remember x and y value of final piece index (white piece)         float temp_y = piece[number_of_grid_cells-1].yPosition();        piece[number_of_grid_cells-1].storePos(piece[i].xPosition(), piece[i].yPosition()); // Store clicked x and y value in final index of piece array        piece[i].storePos(temp_x, temp_y); // Store temp x and y value (the last/previous final index values) in current clicked piece index          piece[number_of_grid_cells-1].design(); // draw the final index piece index (only final piece index is painted white)        piece[i].design(); // Draw a numbered piece of current index      }    }  }} boolean pieceMove(float final_index_piece_x, float final_index_piece_y, float current_index_x, float current_index_y) {  // If both x values from clicked and white piece have same value meaning in same horizontal column   // AND   current clicked y value is equal to white piece y value - piece side lenght  OR  current clicked y value + piece side lenght is egual to white piece y   if (current_index_x == final_index_piece_x && (current_index_y == final_index_piece_y-piece_side_length || (current_index_y == final_index_piece_y+piece_side_length))) return true;  // If both y values from clicked and white piece have same value meaning in same vertical column   // AND   current clicked x value is equal to white piece x value - piece side lenght  OR  current clicked x value + piece side lenght is egual to white piece x   else if (current_index_y == final_index_piece_y && (current_index_x == final_index_piece_x-piece_side_length || (current_index_x == final_index_piece_x+piece_side_length))) return true;  else return false;} class PuzzlePiece {     int piece_number;  float x_pos, y_pos;   PuzzlePiece(int _piece_nr, float _xp, float _yp) {    piece_number = _piece_nr;    x_pos = _xp;    y_pos = _yp;  }     void storePos(float _xp, float _yp) {    x_pos = _xp;    y_pos = _yp;  }   int pieceNumber() {    return piece_number;  }   float xPosition() {    return x_pos;  }   float yPosition() {    return y_pos;  }   void design() {    noStroke();    fill(piece_color);    if (piece_number == number_of_grid_cells-1) fill(background_color);       rect(x_pos+1, y_pos+1, piece_side_length-1, piece_side_length-1);    if (piece_number != number_of_grid_cells-1) {      fill(0); // Black text shadow      text(piece_number+1, x_pos+piece_side_length/2+1, y_pos+piece_side_length/2+textAscent()/2);      fill(255);      text(piece_number+1, x_pos+piece_side_length/2, y_pos+piece_side_length/2+textAscent()/2);      stroke(piece_shadow_dark);      line(x_pos+piece_side_length-1, y_pos+1, x_pos+piece_side_length-1, y_pos+piece_side_length-1); // Right side shadow      line(x_pos+2, y_pos+piece_side_length, x_pos+piece_side_length-1, y_pos+piece_side_length); // Bottom side shadow      stroke(piece_shadow_light);      line(x_pos+2, y_pos-1, x_pos+2, y_pos+piece_side_length); // Left bright      line(x_pos+2, y_pos+1, x_pos+piece_side_length-1, y_pos+1); // Upper bright    }  }}`

It can be played on line :
here.

### Processing Python mode

Translation of: Processing
`# Set the number of cells of the board here 9, 16, 25 etcnum_grid_cells = 16piece_color = color(255, 175, 0)background_color = color(235, 231, 178)piece_shadow_dark = color(206, 141, 0)piece_shadow_light = color(255, 214, 126) def setup():    global piece, piece_number, row_length, piece_side_length    size(400, 400)  # Window size width and height must be egual    background(200, 50, 0)    row_length = int(sqrt(num_grid_cells))    piece_side_length = width / row_length    textSize(piece_side_length / 2.7)    textAlign(CENTER)    # Setting the x and y values for each cell on grid    xy_val = []    t = 0    for i in range(0, num_grid_cells, row_length):        for j in range(row_length):            xy_val.append((j * piece_side_length,                           t * piece_side_length))        t += 1    piece = []  # Puzzle piece objects    placed = [False] * num_grid_cells  # to help placing the pieces randomly    piece_number = 0    # Placing pieces randomly in grid    while (piece_number < num_grid_cells):        p = int(random(0, num_grid_cells))        # Once placed will be set to True to avoid adding again at this location        if not placed[p]:            # Creating the piece objects list            piece.append(PuzzlePiece(piece_number, xy_val[p][0], xy_val[p][1]))            placed[p] = True            piece[piece_number].design()  # Draw newly create piece object            piece_number += 1 def draw():    # Search all piece object indexes and verify which one is mouse pressed    for i in range(num_grid_cells):        if (mousePressed and                piece[i].x <= mouseX <= piece[i].x + piece_side_length and                piece[i].y <= mouseY <= piece[i].y + piece_side_length and                piece[i].piece_number != 15):            if (pieceMove(piece[num_grid_cells - 1].x, piece[num_grid_cells - 1].y, piece[i].x, piece[i].y)):                # Remember x and y value of final piece index (white piece)                temp_x = int(piece[num_grid_cells - 1].x)                temp_y = int(piece[num_grid_cells - 1].y)                # Store clicked x and y value in final index of piece list                piece[num_grid_cells - 1].set_pos(piece[i].x, piece[i].y)                # Store temp x and y value (the last/previous final index                # values) in current clicked piece                piece[i].set_pos(temp_x, temp_y)                # draw the final index piece index (only final piece index is                # painted white)                piece[num_grid_cells - 1].design()                piece[i].design()  # Draw a numbered piece of current index def pieceMove(final_index_piece_x, final_index_piece_y, current_index_x, current_index_y):    # If both x's from clicked and white piece have same value meaning in same horizontal column    # AND current clicked y value is equal to white piece y value - piece side lenght OR    # current clicked y value + piece side lenght is egual to white piece y    if (current_index_x == final_index_piece_x and (current_index_y == final_index_piece_y - piece_side_length or                                                    (current_index_y == final_index_piece_y + piece_side_length))):        return True    # If both y's from clicked and white piece have same value meaning in same vertical column AND current clicked x value    # is equal to white piece x value - piece side lenght OR current clicked x value + piece side lenght is    # egual to white piece x    elif (current_index_y == final_index_piece_y and (current_index_x == final_index_piece_x - piece_side_length or                                                      (current_index_x == final_index_piece_x + piece_side_length))):        return True    else:        return False class PuzzlePiece:     def __init__(self, pn, xp, yp):        self.piece_number = pn        self.x = xp        self.y = yp     def set_pos(self, xp, yp):        self.x = xp        self.y = yp     def design(self):        noStroke()        fill(piece_color)        if (self.piece_number == num_grid_cells - 1):            fill(background_color)        rect(self.x + 1, self.y + 1,             piece_side_length - 1, piece_side_length - 1)        if (self.piece_number != num_grid_cells - 1):            fill(0)  # Black text shadow            text(self.piece_number + 1, self.x + piece_side_length / 2 + 2,                 self.y + piece_side_length / 2 + textAscent() / 2)            fill(255)            text(self.piece_number + 1, self.x + piece_side_length / 2,                 self.y + piece_side_length / 2 + textAscent() / 2)            stroke(piece_shadow_dark)            line(self.x + piece_side_length - 1, self.y + 1, self.x +                 piece_side_length - 1, self.y + piece_side_length - 1)  # Right side shadow            line(self.x + 2, self.y + piece_side_length, self.x +                 piece_side_length - 1, self.y + piece_side_length)  # Bottom side shadow            stroke(piece_shadow_light)            # Left bright            line(self.x + 2, self.y - 1, self.x + 2,                 self.y + piece_side_length)            # Upper bright            line(self.x + 2, self.y + 1, self.x +                 piece_side_length - 1, self.y + 1)`

## PureBasic

Console version. All Puzzles are solvable. #Difficulty is the number of shuffle (default 10). Numbers are displayed in Hexadecimal (ie 1 to F) Default controls are u,d,l,r

` #difficulty=10 ;higher is harder#up="u"#down="d"#left="l"#right="r"OpenConsole("15 game"):EnableGraphicalConsole(1)Global Dim Game.i(3,3)Global hole.point Procedure NewBoard()    num.i=0    For j=0 To 3        For i=0 To 3            Game(i,j)=num            num+1        Next i    Next jEndProcedureProcedure.s displayBoard()    For j=0 To 3        For i=0 To 3            ConsoleLocate(i,j)            If Game(i,j)=0:Print(" "):Continue:EndIf            Print(Hex(Game(i,j)))        Next i    Next j    PrintN("")    Print("Your Choice Up :"+#up+", Down :"+#down+", Left :"+#left+" Or Right :"+#right+" ")    Repeat        keypress\$=Inkey()    Until keypress\$<>""    keypress\$=LCase(keypress\$)    ProcedureReturn keypress\$EndProcedureProcedure UpdateBoard(key\$)    If key\$=#up And hole\y<3        Swap game(hole\x,hole\y),game(hole\x,hole\y+1):hole\y+1    ElseIf key\$=#down And hole\y        Swap game(hole\x,hole\y),game(hole\x,hole\y-1):hole\y-1    ElseIf key\$=#left And hole\x<3        Swap game(hole\x,hole\y),game(hole\x+1,hole\y):hole\x+1    ElseIf key\$=#right And hole\x        Swap game(hole\x,hole\y),game(hole\x-1,hole\y):hole\x-1    EndIfEndProcedureProcedure TestGameWin()    For j=0 To 3        For i=0 To 3            num+1            If game(i,j)=num:win+1:EndIf        Next i    Next j    If win=15:ProcedureReturn 1:EndIfEndProcedureProcedure ShuffleBoard(difficulty.i)    Dim randomKey\$(3)    randomkey\$(0)=#up:randomkey\$(1)=#down:randomkey\$(2)=#left:randomkey\$(3)=#right    For i=1 To difficulty       UpdateBoard(randomKey\$(Random(3)))     Next iEndProcedureNewBoard()ShuffleBoard(#difficulty)Repeat    choice\$=displayBoard()    UpdateBoard(choice\$)Until TestGameWin()Print("Won !")CloseConsole() `
```1234
5678
9ABC
DEF

Your Choice Up :u, Down :d, Left :l Or Right :r
```

## Python

### Python: Original, with output

Works with: Python version 3.X

unoptimized

` ''' Structural Game for 15 - Puzzle with different difficulty levels'''from random import randint  class Puzzle:    def __init__(self):        self.items = {}        self.position = None     def main_frame(self):        d = self.items        print('+-----+-----+-----+-----+')        print('|%s|%s|%s|%s|' % (d[1], d[2], d[3], d[4]))        print('+-----+-----+-----+-----+')        print('|%s|%s|%s|%s|' % (d[5], d[6], d[7], d[8]))        print('+-----+-----+-----+-----+')        print('|%s|%s|%s|%s|' % (d[9], d[10], d[11], d[12]))        print('+-----+-----+-----+-----+')        print('|%s|%s|%s|%s|' % (d[13], d[14], d[15], d[16]))        print('+-----+-----+-----+-----+')     def format(self, ch):        ch = ch.strip()        if len(ch) == 1:            return '  ' + ch + '  '        elif len(ch) == 2:            return '  ' + ch + ' '        elif len(ch) == 0:            return '     '     def change(self, to):        fro = self.position        for a, b in self.items.items():            if b == self.format(str(to)):                to = a                break        self.items[fro], self.items[to] = self.items[to], self.items[fro]        self.position = to     def build_board(self, difficulty):        for i in range(1, 17):            self.items[i] = self.format(str(i))        tmp = 0        for a, b in self.items.items():            if b == '  16 ':                self.items[a] = '     '                tmp = a                break        self.position = tmp        if difficulty == 0:            diff = 10        elif difficulty == 1:            diff = 50        else:            diff = 100        for _ in range(diff):            lst = self.valid_moves()            lst1 = []            for j in lst:                lst1.append(int(j.strip()))            self.change(lst1[randint(0, len(lst1)-1)])     def valid_moves(self):        pos = self.position        if pos in [6, 7, 10, 11]:            return self.items[pos - 4], self.items[pos - 1],\                   self.items[pos + 1], self.items[pos + 4]        elif pos in [5, 9]:            return self.items[pos - 4], self.items[pos + 4],\                   self.items[pos + 1]        elif pos in [8, 12]:            return self.items[pos - 4], self.items[pos + 4],\                   self.items[pos - 1]        elif pos in [2, 3]:            return self.items[pos - 1], self.items[pos + 1], self.items[pos + 4]        elif pos in [14, 15]:            return self.items[pos - 1], self.items[pos + 1],\                  self.items[pos - 4]        elif pos == 1:            return self.items[pos + 1], self.items[pos + 4]        elif pos == 4:            return self.items[pos - 1], self.items[pos + 4]        elif pos == 13:            return self.items[pos + 1], self.items[pos - 4]        elif pos == 16:            return self.items[pos - 1], self.items[pos - 4]     def game_over(self):        flag = False        for a, b in self.items.items():            if b == '     ':                pass            else:                if a == int(b.strip()):                    flag = True                else:                    flag = False        return flag  g = Puzzle()g.build_board(int(input('Enter the difficulty : 0 1 2\n2 '                        '=> highest 0=> lowest\n')))g.main_frame()print('Enter 0 to exit')while True:    print('Hello user:\nTo change the position just enter the no. near it')    lst = g.valid_moves()    lst1 = []    for i in lst:        lst1.append(int(i.strip()))        print(i.strip(), '\t', end='')    print()    x = int(input())    if x == 0:        break    elif x not in lst1:        print('Wrong move')    else:        g.change(x)    g.main_frame()    if g.game_over():        print('You WON')        break `
```Enter the difficulty : 0 1 2
2 => highest 0=> lowest
1
+-----+-----+-----+-----+
|  1  |  7  |  2  |  4  |
+-----+-----+-----+-----+
|  5  |  6  |  3  |  12 |
+-----+-----+-----+-----+
|  9  |  8  |  11 |  15 |
+-----+-----+-----+-----+
|  13 |  10 |  14 |     |
+-----+-----+-----+-----+
Enter 0 to exit
Hello user:
To change the position just enter the no. near it
14 	15
14
+-----+-----+-----+-----+
|  1  |  7  |  2  |  4  |
+-----+-----+-----+-----+
|  5  |  6  |  3  |  12 |
+-----+-----+-----+-----+
|  9  |  8  |  11 |  15 |
+-----+-----+-----+-----+
|  13 |  10 |     |  14 |
+-----+-----+-----+-----+
Hello user:
To change the position just enter the no. near it
10 	14 	11
```

### Python: using tkinter

` ''' Python 3.6.5 code using Tkinter graphical user interface.'''  from tkinter import *from tkinter import messageboximport random # ************************************************ class Board:    def __init__(self, playable=True):        while True:            # list of text for game squares:            self.lot = [str(i) for i in range(1,16)] + ['']            if not playable:                break            # list of text for game squares randomized:            random.shuffle(self.lot)            if self.is_solvable():                break         # game board is 2D array of game squares:        self.bd = []        i = 0        for r in range(4):            row = []            for c in range(4):                row.append(Square(r,c,self.lot[i]))                i += 1            self.bd.append(row)     # How to check if an instance of a 15 puzzle    # is solvable is explained here:    # https://www.geeksforgeeks.org/check-instance-15-puzzle-solvable/    # I only coded for the case where N is even.    def is_solvable(self):        inv = self.get_inversions()        odd = self.is_odd_row()        if inv % 2 == 0 and odd:            return True        if inv % 2 == 1 and not odd:            return True        return False     def get_inversions(self):        cnt = 0        for i, x in enumerate(self.lot[:-1]):            if x != '':                for y in self.lot[i+1:]:                    if y != '' and int(x) > int(y):                        cnt += 1        return cnt     # returns True if open square is in odd row from bottom:    def is_odd_row(self):        idx = self.lot.index('')        return idx in [4,5,6,7,12,13,14,15]                # returns name, text, and button object at row & col:    def get_item(self, r, c):        return self.bd[r][c].get()     def get_square(self, r, c):        return self.bd[r][c]     def game_won(self):        goal = [str(i) for i in range(1,16)] + ['']        i = 0        for r in range(4):            for c in range(4):                nm, txt, btn = self.get_item(r,c)                if txt != goal[i]:                    return False                i += 1        return True # ************************************************ class Square:       # ['btn00', '0', None]    def __init__(self, row, col, txt):        self.row = row        self.col = col        self.name = 'btn' + str(row) + str(col)        self.txt = txt        self.btn = None     def get(self):            return [self.name, self.txt, self.btn]     def set_btn(self, btn):        self.btn = btn     def set_txt(self, txt):        self.txt = txt # ************************************************ class Game:    def __init__(self, gw):        self.window = gw         # game data:        self.bd = None        self.playable = False         # top frame:        self.top_fr = Frame(gw,                            width=600,                            height=100,                            bg='light green')        self.top_fr.pack(fill=X)         self.hdg = Label(self.top_fr,                         text='  15 PUZZLE GAME  ',                         font='arial 22 bold',                         fg='Navy Blue',                         bg='white')        self.hdg.place(relx=0.5, rely=0.4,                       anchor=CENTER)         self.dir = Label(self.top_fr,                 text="(Click 'New Game' to begin)",                 font='arial 12 ',                 fg='Navy Blue',                 bg='light green')        self.dir.place(relx=0.5, rely=0.8,                       anchor=CENTER)         self.play_btn = Button(self.top_fr,                               text='New \nGame',                               bd=5,                               bg='PaleGreen4',                               fg='White',                               font='times 12 bold',                               command=self.new_game)        self.play_btn.place(relx=0.92, rely=0.5,                       anchor=E)         # bottom frame:        self.btm_fr = Frame(gw,                            width=600,                            height=500,                            bg='light steel blue')        self.btm_fr.pack(fill=X)         # board frame:        self.bd_fr = Frame(self.btm_fr,                           width=400+2,                           height=400+2,                           relief='solid',                           bd=1,                           bg='lemon chiffon')        self.bd_fr.place(relx=0.5, rely=0.5,                         anchor=CENTER)         self.play_game() # ************************************************     def new_game(self):        self.playable = True        self.dir.config(text='(Click on a square to move it)')        self.play_game()     def play_game(self):        # place squares on board:        if self.playable:            btn_state = 'normal'        else:            btn_state = 'disable'        self.bd = Board(self.playable)                       objh = 100  # widget height        objw = 100  # widget width        objx = 0    # x-position of widget in frame        objy = 0    # y-position of widget in frame         for r in range(4):            for c in range(4):                nm, txt, btn = self.bd.get_item(r,c)                bg_color = 'RosyBrown1'                if txt == '':                    bg_color = 'White'                           game_btn = Button(self.bd_fr,                                  text=txt,                                  relief='solid',                                  bd=1,                                  bg=bg_color,                                  font='times 12 bold',                                  state=btn_state,                                  command=lambda x=nm: self.clicked(x))                game_btn.place(x=objx, y=objy,                               height=objh, width=objw)                 sq = self.bd.get_square(r,c)                sq.set_btn(game_btn)                 objx = objx + objw            objx = 0            objy = objy + objh     # processing when a square is clicked:    def clicked(self, nm):        r, c = int(nm[3]), int(nm[4])        nm_fr, txt_fr, btn_fr = self.bd.get_item(r,c)         # cannot 'move' open square to itself:        if not txt_fr:            messagebox.showerror(                'Error Message',                'Please select "square" to be moved')            return         # 'move' square to open square if 'adjacent' to it:                    adjs = [(r-1,c), (r, c-1), (r, c+1), (r+1, c)]        for x, y in adjs:            if 0 <= x <= 3 and 0 <= y <= 3:                nm_to, txt_to, btn_to = self.bd.get_item(x,y)                if not txt_to:                    sq = self.bd.get_square(x,y)                    sq.set_txt(txt_fr)                    sq = self.bd.get_square(r,c)                    sq.set_txt(txt_to)                    btn_to.config(text=txt_fr,                                  bg='RosyBrown1')                    btn_fr.config(text=txt_to,                                  bg='White')                    # check if game is won:                                  if self.bd.game_won():                        ans = messagebox.askquestion(                            'You won!!!   Play again?')                        if ans == 'no':                            self.window.destroy()                        else:                            self.new_game()                    return         # cannot move 'non-adjacent' square to open square:        messagebox.showerror(            'Error Message',            'Illigal move, Try again')        return # ************************************************ root = Tk()root.title('15 Puzzle Game')root.geometry('600x600+100+50')root.resizable(False, False)g = Game(root)root.mainloop() `

## QB64

` _TITLE "GUI Sliding Blocks Game "RANDOMIZE TIMER ' get from user the desired board size = sDO    LOCATE CSRLIN, 3: INPUT "(0 quits) Enter your number of blocks per side 3 - 9 you want > ", s    IF s = 0 THEN ENDLOOP UNTIL s > 2 AND s < 10 ' screen setup: based on the square blocks q pixels a sidesq = 540 / s 'square size, shoot for 540 x 540 pixel board displaySCREEN _NEWIMAGE(q * s + 1, q * s + 1, 32): _SCREENMOVE 360, 60 'initialize board = solutionDIM board(s, s)FOR r = 1 TO s    FOR c = 1 TO s        board(c, r) = c + (r - 1) * s    NEXTNEXTboard(s, s) = 0: c0 = s: r0 = s 'scramble board for puzzleFOR i = 0 TO s ^ 5 ' mix blocks    SELECT CASE INT(RND * 4) + 1        CASE 1: IF c0 < s THEN board(c0, r0) = board(c0 + 1, r0): board(c0 + 1, r0) = 0: c0 = c0 + 1        CASE 2: IF c0 > 1 THEN board(c0, r0) = board(c0 - 1, r0): board(c0 - 1, r0) = 0: c0 = c0 - 1        CASE 3: IF r0 < s THEN board(c0, r0) = board(c0, r0 + 1): board(c0, r0 + 1) = 0: r0 = r0 + 1        CASE 4: IF r0 > 1 THEN board(c0, r0) = board(c0, r0 - 1): board(c0, r0 - 1) = 0: r0 = r0 - 1    END SELECTNEXT t = TIMER: update = -1 'OK user here you go!DO    IF update THEN 'display status and determine if solved        solved = -1: update = 0        FOR r = 1 TO s            FOR c = 1 TO s                IF board(c, r) THEN                    IF board(c, r) <> (r - 1) * s + c THEN solved = 0                    COLOR _RGB32(255, 255, 255), _RGB32(0, 0, 255)                    LINE ((c - 1) * q + 1, (r - 1) * q + 2)-(c * q - 2, r * q - 2), _RGB32(0, 0, 255), BF                    _PRINTSTRING ((c - 1) * q + .4 * q, (r - 1) * q + .4 * q), RIGHT\$(" " + STR\$(board(c, r)), 2)                ELSE                    IF board(s, s) <> 0 THEN solved = 0                    COLOR _RGB32(0, 0, 0), _RGB32(0, 0, 0)                    LINE ((c - 1) * q, (r - 1) * q)-(c * q, r * q), , BF                END IF            NEXT        NEXT        IF solved THEN 'flash the Solved Report until user closes window else report status            _DISPLAY            flash\$ = "Solved!" + STR\$(mc) + " Moves in " + STR\$(INT(TIMER - t)) + " secs."            WHILE 1: _TITLE flash\$: _DELAY .2: _TITLE "  ": _DELAY .2: WEND        ELSE            _TITLE STR\$(mc) + " Moves in " + STR\$(INT(TIMER - t)) + " secs." + STR\$(test)        END IF        _DISPLAY    END IF     'get next mouse click, check if on block next to empty space make move or beep    m = _MOUSEINPUT: mb = _MOUSEBUTTON(1): mx = _MOUSEX: my = _MOUSEY    IF mb AND solved = 0 THEN 'get last place mouse button was down        mb = _MOUSEBUTTON(1): mx = _MOUSEX: my = _MOUSEY        WHILE mb 'left button down, wait for mouse button release            m = _MOUSEINPUT: mb = _MOUSEBUTTON(1): mx = _MOUSEX: my = _MOUSEY        WEND         'convert mouse position to board array (x, y) are we near empty space?        bx = INT(mx / q) + 1: by = INT(my / q) + 1: update = -1        IF bx = c0 + 1 AND by = r0 THEN            board(c0, r0) = board(c0 + 1, r0): board(c0 + 1, r0) = 0: c0 = c0 + 1: mc = mc + 1        ELSEIF bx = c0 - 1 AND by = r0 THEN            board(c0, r0) = board(c0 - 1, r0): board(c0 - 1, r0) = 0: c0 = c0 - 1: mc = mc + 1        ELSEIF bx = c0 AND by = r0 + 1 THEN            board(c0, r0) = board(c0, r0 + 1): board(c0, r0 + 1) = 0: r0 = r0 + 1: mc = mc + 1        ELSEIF bx = c0 AND by = r0 - 1 THEN            board(c0, r0) = board(c0, r0 - 1): board(c0, r0 - 1) = 0: r0 = r0 - 1: mc = mc + 1        ELSE            BEEP        END IF    END IFLOOP `

## 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.

` ( moves: 0 - up, 1 - down, 2 - left, 3 - right ) [ stack ]                            is 15.solver ( --> [ ) [ 0 swap find ]                      is find-empty ( board --> n ) [ over + dup -1 >  over 4 < and iff  [ nip true ] else  [ drop false ] ] is try-nudge ( coord delta --> coord ? ) [ dip [ dup find-empty 4 /mod ]  2 /mod 2 * 1 -  swap iff try-nudge else  [ dip swap    try-nudge    dip swap ]  dip [ swap 4 * + ] ]               is try-move-target ( board move --> board target ? ) [ 2dup peek dip  [ -1 unrot poke    0 over find ]  unrot poke  0 swap -1 over find poke ]         is move-to ( board target --> board ) [ try-move-target  iff [ move-to true ]  else [ drop false ] ]              is try-move ( board move --> board ? ) [ [] 15 times  [ i^ 1+ join ]  0 join ]                  constant is <board> ( --> board ) [ <board>  410 times ( 2 * the upper bound on the maximum moves required to solve )  [ 4 random    try-move drop ] ]                is <random-board> ( --> board ) [ peek dup iff  [ number\$    dup size 1 =    if [ space swap join ] ]  else [ drop \$ '  ' ]  echo\$ ]                            is echo-cell ( board n --> ) [ 4 * 4 times  [ say '| '    2dup i^ +    echo-cell    say ' ' ]  say '|'  2drop ]                            is echo-row ( board n --> ) [ 4 times  [ say '+----+----+----+----+' cr    dup i^ echo-row cr ]  say '+----+----+----+----+' cr  drop ]                             is echo-board ( board --> ) [ <board> = ]                        is solved? ( board --> ? ) [ \$ 'Moves: ' input ]                is input-moves ( --> \$ ) [ dup char w = iff  [ drop 0 true ] done  dup char s = iff  [ drop 1 true ] done  dup char a = iff  [ drop 2 true ] done  dup char d = iff  [ drop 3 true ] done  false ]                            is char-to-move ( c --> move true | c false ) [ input-moves witheach  [ char-to-move    if try-move    drop ] ]                         is player-move ( board --> board ) [ ]'[ 15.solver put  [ cr dup echo-board    dup solved? not while    15.solver share do    again ]  15.solver release drop ]           is 15-solve-with ( board 'solver --> board ) [ <random-board>  15-solve-with player-move ]        is 15-puzzle ( --> ) 15-puzzle `

## 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.

` puz15<-function(scramble.length=100){  m=matrix(c(1:15,0),byrow=T,ncol=4)  scramble=sample(c("w","a","s","d"),scramble.length,replace=T)  for(i in 1:scramble.length){    m=move(m,scramble[i])  }  win=F  turn=0  while(!win){    print.puz(m)    newmove=getmove()    if(newmove=="w"|newmove=="a"|newmove=="s"|newmove=="d"){      m=move(m,newmove)      turn=turn+1    }    else{      cat("Input not recognized","\n")    }    if(!(F %in% m==matrix(c(1:15,0),byrow=T,ncol=4))){      win=T    }  }  print.puz(m)  cat("\n")  print("You win!")  cat("\n","It took you",turn,"moves.","\n")} getmove<-function(){  direction<-readline(prompt="Move:")  return(direction)}move<-function(m,direction){  if(direction=="w"){    m=move.u(m)  }  else if(direction=="s"){    m=move.d(m)  }  else if(direction=="a"){    m=move.l(m)  }  else if(direction=="d"){    m=move.r(m)  }  return(m)}move.u<-function(m){  if(0 %in% m[4,]){}  else{    pos=which(m==0)    m[pos]=m[pos+1]    m[pos+1]=0  }  return(m)}move.d<-function(m){  if(0 %in% m[1,]){}  else{    pos=which(m==0)    m[pos]=m[pos-1]    m[pos-1]=0  }    return(m)}move.l<-function(m){  if(0 %in% m[,4]){return(m)}  else{return(t(move.u(t(m))))}}move.r<-function(m){  if(0 %in% m[,1]){return(m)}  else{return(t(move.d(t(m))))}}print.puz<-function(m){  cat("+----+----+----+----+","\n")  for(r in 1:4){    string="|"    for(c in 1:4){      if(m[r,c]==0)        string=paste(string,"    |",sep="")      else if(m[r,c]<10)        string=paste(string,"  ",m[r,c]," |",sep="")      else        string=paste(string," ",m[r,c]," |",sep="")    }    cat(string,"\n","+----+----+----+----+","\n",sep="")  }} `

Sample output:

```> puz15(scramble.length=4)
+----+----+----+----+
|  1 |  2 |  3 |  4 |
+----+----+----+----+
|  5 |  6 |  7 |  8 |
+----+----+----+----+
|  9 | 10 |    | 12 |
+----+----+----+----+
| 13 | 14 | 11 | 15 |
+----+----+----+----+
Move:w
+----+----+----+----+
|  1 |  2 |  3 |  4 |
+----+----+----+----+
|  5 |  6 |  7 |  8 |
+----+----+----+----+
|  9 | 10 | 11 | 12 |
+----+----+----+----+
| 13 | 14 |    | 15 |
+----+----+----+----+
Move:a
+----+----+----+----+
|  1 |  2 |  3 |  4 |
+----+----+----+----+
|  5 |  6 |  7 |  8 |
+----+----+----+----+
|  9 | 10 | 11 | 12 |
+----+----+----+----+
| 13 | 14 | 15 |    |
+----+----+----+----+

[1] "You win!"

It took you 2 moves.
```

## Racket

This is a GUI game; and there are difficulties getting screen shots onto RC. Use the arrow keys to slide the blank square.

It uses the `2htdp/universe` package.

`#lang racket/base(require 2htdp/universe 2htdp/image racket/list racket/match) (define ((fifteen->pict (finished? #f)) fifteen)  (for/fold ((i (empty-scene 0 0))) ((r 4))    (define row      (for/fold ((i (empty-scene 0 0))) ((c 4))        (define v (list-ref fifteen (+ (* r 4) c)))        (define cell          (if v              (overlay/align               "center" "center"               (rectangle 50 50 'outline (if finished? "white" "blue"))               (text (number->string v) 30 "black"))              (rectangle 50 50 'solid (if finished? "white" "powderblue"))))        (beside i cell)))    (above i row))) (define (move-space fifteen direction)  (define idx (for/first ((i (in-naturals)) (x fifteen) #:unless x) i))  (define-values (row col) (quotient/remainder idx 4))  (define dest (+ idx (match direction                        ['l #:when (> col 0) -1]                        ['r #:when (< col 3)  1]                        ['u #:when (> row 0) -4]                        ['d #:when (< row 3)  4]                        [else 0])))    (list-set (list-set fifteen idx (list-ref fifteen dest)) dest #f)) (define (key-move-space fifteen a-key)  (cond [(key=? a-key "left") (move-space fifteen 'l)]        [(key=? a-key "right") (move-space fifteen 'r)]        [(key=? a-key "up") (move-space fifteen 'u)]        [(key=? a-key "down") (move-space fifteen 'd)]        [else fifteen])) (define (shuffle-15 fifteen shuffles)  (for/fold ((rv fifteen)) ((_ shuffles))    (move-space rv (list-ref '(u d l r) (random 4))))) (define fifteen0 '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #f)) (define (solved-world? w) (equal? w fifteen0)) (big-bang (shuffle-15 fifteen0 200)          (name "Fifteen")          (to-draw (fifteen->pict))          (stop-when solved-world? (fifteen->pict #t))          (on-key key-move-space))`

## Raku

(formerly Perl 6)

Works with: Rakudo version 2018.06

Most of this is interface code. Reused substantial portions from the 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.

`use Term::termios; constant \$saved   = Term::termios.new(fd => 1).getattr;constant \$termios = Term::termios.new(fd => 1).getattr;# raw mode interferes with carriage returns, so# set flags needed to emulate it manually\$termios.unset_iflags(<BRKINT ICRNL ISTRIP IXON>);\$termios.unset_lflags(< ECHO ICANON IEXTEN ISIG>);\$termios.setattr(:DRAIN); # reset terminal to original setting on exitEND { \$saved.setattr(:NOW) } constant n    = 4; # board sizeconstant cell = 6; # cell width constant \$top = join '─' x cell, '┌', '┬' xx n-1, '┐';constant \$mid = join '─' x cell, '├', '┼' xx n-1, '┤';constant \$bot = join '─' x cell, '└', '┴' xx n-1, '┘'; my %dir = (   "\e[A" => 'up',   "\e[B" => 'down',   "\e[C" => 'right',   "\e[D" => 'left',); my @solved = [1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,' '];my @board;new(); sub new () {    loop {       @board = shuffle();       last if parity-ok(@board);   }} sub parity-ok (@b) {    my \$row = @b.first(/' '/,:k);    my \$col = @b[\$row].first(/' '/,:k);    so ([3,3] <<->> [\$row,\$col]).sum %% 2;} sub shuffle () {    my @c = [1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,' '];    for (^16).pick(*) -> \$y, \$x {        my (\$yd, \$ym, \$xd, \$xm) = (\$y div n, \$y mod n, \$x div n, \$x mod n);        @(@c[\$ym;\$yd],@c[\$xm;\$xd]) = @(@c[\$xm;\$xd],@c[\$ym;\$yd]);    }    @c;} sub row (@row) { '│' ~ (join '│', @row».&center) ~ '│' } sub center (\$s){    my \$c   = cell - \$s.chars;    my \$pad = ' ' x ceiling(\$c/2);    sprintf "%{cell}s", "\$s\$pad";} sub draw-board {    run('clear');    print qq:to/END/;  	Press direction arrows to move. 	Press q to quit. Press n for a new puzzle. 	\$top	{ join "\n\t\$mid\n\t", map { .&row }, @board }	\$bot 	{ (so @board ~~ @solved) ?? 'Solved!!' !! '' }END} sub slide (@c is copy) {    my \$t = (grep { /' '/ }, :k, @c)[0];    return @c unless \$t and \$t > 0;    @c[\$t,\$t-1] = @c[\$t-1,\$t];    @c;} proto sub move (|) {*}; multi move('up') {    map { @board[*;\$_] = reverse slide reverse @board[*;\$_] }, ^n;} multi move('down') {    map { @board[*;\$_] = slide @board[*;\$_] }, ^n;} multi move('left') {    map { @board[\$_] = reverse slide reverse @board[\$_] }, ^n;} multi move('right') {    map { @board[\$_] = slide @board[\$_] }, ^n;} loop {    draw-board;     # Read up to 4 bytes from keyboard buffer.    # Page navigation keys are 3-4 bytes each.    # Specifically, arrow keys are 3.    my \$key = \$*IN.read(4).decode;     move %dir{\$key} if so %dir{\$key};    last if \$key eq 'q'; # (q)uit    new() if \$key eq 'n';}`

Sample screen shot:

```	Press direction arrows to move.

Press q to quit. Press n for a new puzzle.

┌──────┬──────┬──────┬──────┐
│  2   │  1   │  10  │  14  │
├──────┼──────┼──────┼──────┤
│  15  │  11  │  12  │      │
├──────┼──────┼──────┼──────┤
│  13  │  3   │  6   │  7   │
├──────┼──────┼──────┼──────┤
│  9   │  4   │  5   │  8   │
└──────┴──────┴──────┴──────┘
```

## 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`

## Red

` Red [Needs: 'view]system/view/screens/1/pane/-1/visible?: false ;; suppress console window;; because this code is heavily influenced / (copied from ... );; by the rebol version ( which actually works with red as well with;; some minimal changes ) i added some comments to make the;; code more readable... ;;-----------------------------------------;; check, if puzzle is solved, OK button closes program;;-----------------------------------------check: does [;; the text on each tile must be equal to its position:  repeat pos 15 [ unless pos = to-integer lay/pane/:pos/text  [exit] ]  view/flags [  title "SuCCeSS"                  text "You solved the puzzle ! "  button "OK"  [quit]    ] [modal popup]    ];;--------------------------------------;; actually only changes text / Number on tile / button;;---------------------------------------changeTiles: func [ f ][  ;; this is tricky, check if e(mpty) button is one in of the  ;; four valid positions , by simply subtracting their offsets...else exit ( no change)  unless find  [ 0x52 0x-52 52x0 -52x0 ] f/offset - e/offset [return false]  e/text: f/text  ;; empty button gets Number from clicked button  e/visible?: true  ;; empty gets visible  f/visible?: false ;; clicked button gets invisible  e: f    ;; e(mpty) refers now to clicked button  return true ;; true - change has happened];;-----------------------------------------;; function which is executed, when button is clicked;; argument is face - button;;-------------------------------------bClicked: func [f][   if changeTiles f [check] ] ;; define the window:;; style is a prototype definition for a button elementwin:   [ title "15 Puzzle"		backdrop silver		style btn: button 40x40  [bClicked face] font-size 15 bold ];; generate 1..15 buttons with number repeat nr 15 [     repend win  [ 'btn form nr ] ;; repend reduces and then appends..     if  0 = mod nr 4  [  append win  'return ] ;; "carriage return" in window after 4 buttons	]  ;; append empty / hidden button no 16 and return/quit button:	append win [ e: btn "16" hidden return  button "Quit" [quit]] lay: layout win;; define win as layout, so  each button can now be addressed as;; layout/pane/index ( 1..15 - redbol is 1 based ) flip: 0 ;; start random generator, otherwise we always get the same puzzlerandom/seed now/time/precise ;; lets flip random tiles a few times to initialize the puzzle;; ( actually numbers < 100 are quite easy to solve ) while [ flip < 40 ] [ if changeTiles lay/pane/(random 16)  [ flip:  flip  + 1 ]  ] ;; show the window...      view  lay  `

## REXX

This REXX version allows the user to specify the size of the puzzle   (N,   where   NxN   is the size of the puzzle).

With some more complexity,   the REXX computer program could be changed to allow multiple─tile moves   (so that,
for instance,   three tiles could be slid to the right).

Over half of the REXX program has to do with input validation and presentation of the puzzle (grid).

`/*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.*/if datatype(seed, 'W')  then call random ,,seed  /*use repeatability seed for RANDOM BIF*/nh= N**2;   @.=;    nn= nh - 1;    w= length(nn) /*define/initialize some handy values. */\$=                                               /*\$: will hold the solution for testing*/       do i=1  for nn;  \$= \$  i                  /* [◄]  build a solution for testing.  */       end   /*i*/done= \$                                          /* [↓]  scramble the tiles in puzzle.  */       do j=1  for nn;  a= random(1, words(\$) );    @.j= word(\$, a);   \$= delword(\$, a, 1)       end   /*j*/                             /*═══════════════════ play the 15─puzzle 'til done or quit.*/   do  until puzz==done & @.nh==''               /*perform moves until puzzle is solved.*/   call getmv                                    /*get user's move(s)  and  validate it.*/   if errMsg\==''  then do;  say sep errMsg;       iterate        /*possible error msg? */                        end   call showGrid 0                               /*don't display puzzle, just find hole.*/   if wordpos(x, !)==0  then do;  say sep  'tile '     x     " can't be moved.";   iterate                             end   @.hole= x;    @.tile=   call showGrid 0                               /*move specified tile ───► puzzle hole.*/   end   /*until*/           /*═════════════════════════════════════════════════════════*/ call showGrid 1;    say;     say sep  'Congratulations!   The'      nn"-puzzle is solved."exit 0                                           /*stick a fork in it,  we're all done. *//*──────────────────────────────────────────────────────────────────────────────────────*/getmv: x= 0;   sep= copies('─', 8);     pad= left('', 1 + length(sep) )   /*pad=9 blanks*/       prompt= sep    'Please enter a tile number  or  numbers '     sep     " (or Quit)."       if queued()==0  then do;    say;    call showGrid 1;     say;      say prompt                            end       parse pull x . 1 ox . 1 . zx;   upper x   /*obtain a number (or numbers) from CL.*/       if abbrev('QUIT', x, 1)  then do;   say;   say;    say sep  "quitting.";      exit                                     end       if words(zx)>0  then do;  parse var  zx    xq;     queue xq                            end                  /* [↑]  Extra moves?  Stack for later. */              select                             /* [↓]  Check for possible errors/typos*/              when x==''              then errMsg= "nothing entered."              when \datatype(x, 'N')  then errMsg= "tile number isn't numeric: "        ox              when \datatype(x, 'W')  then errMsg= "tile number isn't an integer: "     ox              when x=0                then errMsg= "tile number can't be zero: "        ox              when x<0                then errMsg= "tile number can't be negative: "    ox              when x>nn               then errMsg= "tile number can't be greater than"  nn              otherwise                    errMsg=              end   /*select*/                   /* [↑]  verify the human entered data. */       return/*──────────────────────────────────────────────────────────────────────────────────────*/showGrid: parse arg show;       !.=;                      x= x/1;       #= 0;        puzz=          top= '╔'copies( copies("═", w)'╦', N);        top= left( top, length(top) -1)"╗"          bar= '╠'copies( copies("═", w)'╬', N);        bar= left( bar, length(bar) -1)"╣"          bot= '╚'copies( copies("═", w)'╩', N);        bot= left( bot, length(bot) -1)"╝"          if show  then say pad top                do   r=1  for N;     z= '║'                 do c=1  for N;     #= #+1;     y= @.#;      puzz= puzz y;        !.r.c= y                 _= right(@.#, w)"║";           z= z || _              /* [↓]  find hole*/                 if @.# == ''  then do;    hole= #;     holeRow= r;     holeCol= c;    end                 if @.# == x   then do;    tile= #;     tileRow= r;     tileCol= c;    end                 end   /*c*/                                           /* [↑]  find  X. */               if show  then do;    say pad z;     if r\==N  then say pad bar;         end               end     /*r*/           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`
output   when using the default input:
```           ╔══╦══╦══╦══╗
║10║ 7║ 8║11║
╠══╬══╬══╬══╣
║ 4║ 3║15║ 1║
╠══╬══╬══╬══╣
║ 9║12║ 2║13║
╠══╬══╬══╬══╣
║14║ 5║ 6║  ║
╚══╩══╩══╩══╝

──────── Please enter a tile number  or  numbers  ────────  (or Quit).
13                           ◄■■■■■■■■■■ user input.
╔══╦══╦══╦══╗
║10║ 7║ 8║11║
╠══╬══╬══╬══╣
║ 4║ 3║15║ 1║
╠══╬══╬══╬══╣
║ 9║12║ 2║  ║
╠══╬══╬══╬══╣
║14║ 5║ 6║13║
╚══╩══╩══╩══╝

──────── Please enter a tile number  or  numbers  ────────  (or Quit).
1  15  3                     ◄■■■■■■■■■■ user input.
╔══╦══╦══╦══╗
║10║ 7║ 8║11║
╠══╬══╬══╬══╣
║ 4║  ║ 3║15║
╠══╬══╬══╬══╣
║ 9║12║ 2║ 1║
╠══╬══╬══╬══╣
║14║ 5║ 6║13║
╚══╩══╩══╩══╝

──────── Please enter a tile number  or  numbers  ────────  (or Quit).
quit                         ◄■■■■■■■■■■ user input.

──────── quitting.
```

## Ring

` # Project : CalmoSoft Fifteen Puzzle Game # Date    : 2018/12/01# Author  : Gal Zsolt (CalmoSoft), Bert Mariani# Email   : [email protected] load "guilib.ring" app1 = new qapp {         stylefusionblack()        empty = 16          nr_moves = 0        nr_sleep = 1        button_size = 4         current_button = 4        temp = 0        flag_init = 0        flag_save = 0        flag_move = 0        button = list(52)           size_button = list(7)        table1 = []         table2 = []         table3 = []        n_degree = 0        nr_degree = [0,90,180,270 ,-90,-180,-270]        n_degreeRight = 0        n_degreeLeft = 0        btn_degree = newlist(52,2)        counter_man = 0         t1 = 0         win1 = new qwidget() {                   move(0,0)                   resize(380,760)                   setwindowtitle("CalmoSoft Fifteen Puzzle Game")                   for n=1 to 52                        for m=1 to 2                             btn_degree[n][m] = 0                        next                  next                     for n = 4 to 7                               size_button[n] = new qpushbutton(win1)                                  {                                                  col = n%4                                                  setgeometry(100+col*40,60,40,40)                                                  settext(string(n) + "x" + string(n))                                                  setclickevent("newsize(" + string(n) + ")")                               }                    next                       btnMoves = new qpushbutton(win1)                  {                                 setgeometry(100,260,80,40)                                 settext("0")                                 show()                   }                   `