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
    }
  }
}


#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 j
EndProcedure
Procedure.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$
EndProcedure
Procedure 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
    EndIf
EndProcedure
Procedure 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:EndIf
EndProcedure
Procedure 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 i
EndProcedure
NewBoard()
ShuffleBoard(#difficulty)
Repeat
    choice$=displayBoard()
    UpdateBoard(choice$)
Until TestGameWin()
Print("Won !")
CloseConsole()


''' 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


_TITLE "GUI Sliding Blocks Game "
RANDOMIZE TIMER

' get from user the desired board size = s
DO
    LOCATE CSRLIN, 3: INPUT "(0 quits) Enter your number of blocks per side 3 - 9 you want > ", s
    IF s = 0 THEN END
LOOP UNTIL s > 2 AND s < 10

' screen setup: based on the square blocks q pixels a sides
q = 540 / s 'square size, shoot for 540 x 540 pixel board display
SCREEN _NEWIMAGE(q * s + 1, q * s + 1, 32): _SCREENMOVE 360, 60

'initialize board = solution
DIM board(s, s)
FOR r = 1 TO s
    FOR c = 1 TO s
        board(c, r) = c + (r - 1) * s
    NEXT
NEXT
board(s, s) = 0: c0 = s: r0 = s

'scramble board for puzzle
FOR 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 SELECT
NEXT

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 IF
LOOP


( 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


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="")
  }
}


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


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 exit
END { $saved.setattr(:NOW) }

constant n    = 4; # board size
constant 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';
}


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 [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 element
win:   [ 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 puzzle
random/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 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


# 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()
                  }

                   scramblebtn = new qpushbutton(win1)
                   {
                                         setgeometry(100,300,160,40)
                                         settext("Scramble")
                                         setclickevent("scramble()")
                   }

                   resetbtn = new qpushbutton(win1)
                   {
                                    setgeometry(100,340,160,40)
                                    settext("Reset")
                                    setclickevent("resettiles()")
                   }

                   savebtn = new qpushbutton(win1)
                   {
                                   setgeometry(100,380,160,40)
                                   settext("Save Game")
                                   setclickevent("pSave()")
                   }

                   playbtn = new qpushbutton(win1)
                   {
                                   setgeometry(100,420,160,40)
                                   settext("Resume Game")
                                   setclickevent("pPlay()")
                   }

                   sleepbtn = new qpushbutton(win1)
                   {
                                   setgeometry(100,460,160,40)
                                   settext("Sleep Time: ")

                   }

                  decbtn = new qpushbutton(win1)
                  {
                               setgeometry(220,460,40,40)
                               settext("<-")
                               setclickevent("pDecSleep()")
                  }

                  incbtn = new qpushbutton(win1)
                  {
                               setgeometry(260,460,40,40)
                               settext("->")
                               setclickevent("pIncSleep()")
                  }

                 rightbtn = new qpushbutton(win1)
                 {
                               setgeometry(100,500,160,40)
                               settext("In the Right Place : ")
                 }

                 timebtn = new qpushbutton(win1)
                 {
                               setgeometry(100,540,160,40)
                               settext("Elapsed Time : ")
                 }

                TimerMan = new qtimer(win1)
               {
                                  setinterval(500)
                                  settimeoutevent("pTime()")
                                  stop()
               }
              newsize(4)
              show()
        }
        exec()
}

Func newlist x, y
     if isstring(x) x=0+x ok
     if isstring(y) y=0+y ok
     alist = list(x)
     for t in alist
         t = list(y)
     next
     return alist

func scramble
       for n= 1 to 1000
            current_button=random(button_size*button_size-1)+1
            up = (empty = (current_button - button_size))
            down = (empty = (current_button + button_size))
            left = ((empty = (current_button - 1)) and ((current_button % button_size) != 1))
            right = ((empty = (current_button + 1)) and ((current_button % button_size) != 0))
            move = up or down or left  or right
            if move = 1
               button[current_button] { temp2 = text() }
               col = empty%button_size
               if col = 0 col = button_size ok
               row = ceil(empty/button_size)
               button[empty] {
                       setgeometry(60+col*40,60+row*40,40,40)
                       rnd = random(6)+1
                       n_degree = nr_degree[rnd]
                       button[empty].setbuttoncolor("yellow")
                       button[empty].settext(temp2)
                       button[empty].setClickEvent("movetile(" + string(empty) +")")
                       btn_degree[empty] [1] = temp2
                       btn_degree[empty] [2] = n_degree
                       }
               button[current_button].setbuttoncolor("yellow")
               btn_degree[current_button][2] = 0
               button[current_button]{settext("")}
               empty = current_button
            ok
       next
       button[button_size*button_size+2]{settext("Here")}
       for n=1 to button_size*button_size
             button[n].setbuttoncolor("yellow")
       next
       table1 = []
       table2 = []
       table3 = []
       for n = 1 to button_size*button_size
             add(table1, button[n].text())
             add(table2, button[n].text())
             add(table3, string(btn_degree[n][2]))
       next
       add(table1, string(empty))
       add(table2, string(empty))
       add(table3, string(empty))
       add(table1, "OK")
       add(table2, "OK")
       add(table3, "OK")
       flag_save = 0
       flag_move = 0
       nr_moves = 0
       btnMoves.settext(string(nr_moves))
       timebtn.settext("Elapsed Time : ")
       t1 = clock()
       rightPlace()
       return

func movetile current_button2
       if (current_button2 = button_size*button_size-1 and button[current_button2].text() = "In")
           pBack()
       else
           see char(7)
           up = (empty = (current_button2 - button_size))
           down = (empty = (current_button2 + button_size))
           left = ((empty = (current_button2- 1)) and ((current_button2 % button_size) != 1))
           right = ((empty = (current_button2 + 1)) and ((current_button2 % button_size) != 0))
           move = up or down or left  or right
           if move = 1
              temp2 = button[current_button2].text()
              btn_degree[empty][1] = temp2
              add(table1, temp2)
              add(table2, string(current_button2))
              col = empty%button_size
              if col = 0 col = button_size ok
              row = ceil(empty/button_size)
              button[empty] {
                                   setgeometry(60+col*40,60+row*40,40,40)
                                   n_degree = btn_degree[current_button2][2]
                                   btn_degree[empty][2] = n_degree
                                   button[empty].setbuttoncolor("orange")
                                   button[empty].settext(temp2)
              }
              add(table3, string(n_degree))
              button[current_button2].setbuttoncolor("cyan")
              button[current_button2]{settext("")}
              empty = current_button2
              nr_moves = nr_moves + 1
              btnMoves.settext(string(nr_moves))
              isGameOver()
           ok
      ok
      flag_move = 1
      pElapsedTime()
      rightPlace()
      return

func resettiles
        n_degree = 0
        empty = button_size*button_size
        for empty = 1 to button_size*button_size-1
             btn_degree[empty][2] = 0
             n_degree = 0
             btn_degree[empty][1] = string(empty)
             button[empty].setstylesheet("background-color:yellow")
             button[empty] {settext(string(empty))}
        next
        button[button_size*button_size].setstylesheet("background-color:yellow")
        button[button_size*button_size] {settext("")}
        table1 = []
        table2 = []
        table3 = []
        for n = 1 to button_size*button_size
             add(table1, button[n].text())
             add(table2, button[n].text())
             add(table3, string(btn_degree[n][2]))
        next
        add(table1, string(empty))
        add(table2, string(empty))
        add(table3, string(empty))
        add(table1, "OK")
        add(table2, "OK")
        add(table3, "OK")
        flag_save = 0
        flag_move = 0
        nr_moves = 0
        btnMoves.settext(string(nr_moves))
        timebtn.settext("Elapsed Time : ")
        t1 = clock()
        rightPlace()
        return

func pHere
        if button[button_size*button_size-1].text() != "" and button[button_size*button_size+2].text() = "Here"
           button[button_size*button_size-1] { temp = text() }
           button[button_size*button_size+2].close()
           button[button_size*button_size+2] = new ButtonWithRotatedText(win1)
           button[button_size*button_size+2] {
           setgeometry(60+(button_size-1)*40,60+(button_size+1)*40,40,40)
           setstylesheet("background-color:yellow")
           btn_degree[button_size*button_size+2][2] = btn_degree[button_size*button_size-1][2]
           n_degree = btn_degree[button_size*button_size+2][2]
           emptysave = empty
           empty = button_size*button_size+2
           btn_degree[empty][1] = temp
           settext(temp)
           }
           n_degree = 0
           empty = button_size*button_size-1
           btn_degree[empty][1] = "In"
           button[button_size*button_size-1]{settext("In")}
           for n = 1 to button_size*button_size
	   button[n].setenabled(false)
           next
           button[button_size*button_size-1].setenabled(true)
           scramblebtn.setenabled(false)
           resetbtn.setenabled(false)
           savebtn.setenabled(false)
           playbtn.setenabled(false)
           empty = emptysave
        ok

func pBack
        button[button_size*button_size+2] { temp = text() }
        n_degree = btn_degree[button_size*button_size+2][2]
        btn_degree[button_size*button_size-1][2] = btn_degree[button_size*button_size+2][2]
        emptysave = empty
        empty = button_size*button_size-1
        btn_degree[empty][1] = temp
        button[button_size*button_size-1] {settext(temp)}
        button[button_size*button_size+2].close()
        button[button_size*button_size+2] = new qpushbutton(win1)
                {
                setgeometry(60+(button_size-1)*40,60+(button_size+1)*40,40,40)
                settext("Here")
                setclickevent("pHere()")
                show()
                }
        for n = 1 to button_size*button_size
              button[n].setenabled(true)
        next
        scramblebtn.setenabled(true)
        resetbtn.setenabled(true)
        savebtn.setenabled(true)
        playbtn.setenabled(true)
        empty = emptysave
        isGameOver()

func rotateleft
        if button[button_size*button_size+2].text() != "Here"
           button[button_size*button_size+2].close()
           button[button_size*button_size+2] = new ButtonWithRotatedText(win1)
                      button[button_size*button_size+2] {
                      setgeometry(60+(button_size-1)*40,60+(button_size+1)*40,40,40)
                      setstylesheet("background-color:yellow")
                      n_degreeLeft = (n_degreeLeft-90)%360
                      n_degree = n_degreeLeft
                      btn_degree[button_size*button_size+2][2] = n_degree
                      emptysave = empty
                      empty = button_size*button_size+2
                      btn_degree[empty][1] = temp
                      button[button_size*button_size+2]{settext(temp)}
                      }
                      empty = emptysave
        ok

func rotateright
        if button[button_size*button_size+2].text() != "Here"
           button[button_size*button_size+2].close()
           button[button_size*button_size+2] = new ButtonWithRotatedText(win1)
                      button[button_size*button_size+2] {
                      setgeometry(60+(button_size-1)*40,60+(button_size+1)*40,40,40)
                      setstylesheet("background-color:yellow")
                      n_degreeRight = (n_degreeRight+90)%360
                      n_degree = n_degreeRight
                      btn_degree[button_size*button_size+2][2] = n_degree
                      emptysave = empty
                      empty = button_size*button_size+2
                      btn_degree[empty][1] = temp
                      button[button_size*button_size+2]{settext(temp)}
                      }
                      empty = emptysave
        ok

func newsize current_button
        win1{
                sizenew = current_button%4
                win1.resize(360+sizenew*40,640+sizenew*40)
                if flag_init != 0
                   for nb = 1 to button_size*button_size+3
                         button[nb] {close()}
                   next
                   btnMoves.close()
                ok
                scramblebtn.close()
                resetbtn.close()
                savebtn.close()
                playbtn.close()
                btnMoves.close()
                sleepbtn.close()
                decbtn.close()
                incbtn.close()
                rightbtn.close()
                timebtn.close()

                for n = 1 to current_button*current_button
                     col = n%current_button
                     if col = 0 col = current_button ok
                     row = ceil(n/current_button)
                     button[n] = new ButtonWithRotatedText(win1)
                                       button[n] {
                                       setgeometry(60+col*40,60+row*40,40,40)
                                       button[n].setbuttoncolor("yellow")
                                       n_degree = 0
                                       if n < current_button*current_button
                                          button[n].settext(string(n))
                                       but n = current_button*current_button
                                          button[n].settext("")
                                       ok
                                       setClickEvent("movetile(" + string(n) +")")
                                       }
                next

                btnMoves = new qpushbutton(win1)
                {
                                        setgeometry(100,60+(current_button+1)*40,(current_button-3)*40,40)
                                        setStyleSheet("text-align:center")
                                        settext("0")
                                        show()
                }

                button[current_button*current_button+1] = new qpushbutton(win1)
                {
                                              setgeometry(60+(current_button-2)*40,60+(current_button+1)*40,40,40)
                                              settext("<-")
                                              setclickevent("rotateLeft()")
                                              show()
                }

                button[current_button*current_button+2] = new qpushbutton(win1)
                {
                                             setgeometry(60+(current_button-1)*40,60+(current_button+1)*40,40,40)
                                             settext("Here")
                                             setclickevent("pHere()")
                                             show()
                }

                button[current_button*current_button+3] = new qpushbutton(win1)
                {
                                             setgeometry(60+current_button*40,60+(current_button+1)*40,40,40)
                                             settext("->")
                                             setclickevent("rotateRight()")
                                             show()
                 }

                scramblebtn = new qpushbutton(win1)
                {
                                      setgeometry(100,100+(current_button+1)*40,current_button*40,40)
                                      settext("Scramble")
                                      setclickevent("scramble()")
                                      show()
                }

                resetbtn = new qpushbutton(win1)
                {
                                 setgeometry(100,100+(current_button+2)*40,current_button*40,40)
                                 settext("Reset")
                                 setclickevent("resettiles()")
                                 show()
                }

                savebtn = new qpushbutton(win1)
                {
                                 setgeometry(100,100+(current_button+3)*40,current_button*40,40)
                                 settext("Save Game")
                                 setclickevent("pSave()")
                                 show()
                }

                playbtn = new qpushbutton(win1)
                {
                               setgeometry(100,100+(current_button+4)*40,current_button*40,40)
                               settext("Resume Game")
                               setclickevent("pPlay()")
                               show()
                }

                sleepbtn = new qpushbutton(win1)
                {
                               setgeometry(100,100+(current_button+5)*40,(current_button-2)*40,40)
                               settext("Sleep Time: " + string(nr_sleep) + " s")
                               show()
                }

                decbtn = new qpushbutton(win1)
                {
                               setgeometry(100+(current_button-2)*40,100+(current_button+5)*40,40,40)
                               settext("<-")
                               setclickevent("pDecSleep()")
                               show()
                }

                incbtn = new qpushbutton(win1)
                {
                               setgeometry(100+(current_button-1)*40,100+(current_button+5)*40,40,40)
                               settext("->")
                               setclickevent("pIncSleep()")
                               show()
                }

               rightbtn = new qpushbutton(win1)
                {
                               setgeometry(100,100+(current_button+6)*40,current_button*40,40)
                               settext("In the Right Place : ")
                               show()
                }

                timebtn = new qpushbutton(win1)
                {
                               setgeometry(100,100+(current_button+7)*40,current_button*40,40)
                               settext("Elapsed Time : ")
                               show()
                }

                table1 = []
                table2 = []
                table3 = []
                for n = 1 to button_size*button_size
                      add(table1, button[n].text())
                      add(table2, button[n].text())
                      add(table3, string(0))
                next
                add(table1, string(empty))
                add(table2, string(empty))
                add(table3, string(empty))
                add(table1, "OK")
                add(table2, "OK")
                add(table3, "OK")
                empty = current_button*current_button
                button_size = current_button
                flag_init = 1
                flag_save = 0
                flag_move = 0
                nr_moves = 0
                timebtn.settext("Elapsed Time : ")
                t1 = clock()
                scramble()
                }

func pSave
        textedit1 = list2str(table1)
        textedit2 = list2str(table2)
        textedit3 = list2str(table3)
        chdir(currentdir())
        cName1 = "CalmoSoftPuzzle1.txt"
        cName2 = "CalmoSoftPuzzle2.txt"
        cName3 = "CalmoSoftPuzzle3.txt"
        write(cName1,textedit1)
        write(cName2,textedit2)
        write(cName3,textedit3)
         flag_save = 1
         timebtn.settext("Elapsed Time : ")
         t1 = clock()
         return

func pPlay
        if  flag_save = 0 or flag_move = 0
            warning()
        else
           chdir(currentdir())
           cName1 = "CalmoSoftPuzzle1.txt"
           textedit1 = read(cName1)
           table1 = str2list(textedit1)
           cName2 = "CalmoSoftPuzzle2.txt"
           textedit2 = read(cName2)
           table2 = str2list(textedit2)
           cName3 = "CalmoSoftPuzzle3.txt"
           textedit3 = read(cName3)
           table3 = str2list(textedit3)
           for empty = 1 to button_size*button_size
                button[empty].setbuttoncolor("yellow")
                n_degree = number(table3[empty])
                btn_degree[empty][1] = table1[empty]
                button[empty] {settext(table1[empty])}
           next
           empty = number(table1[button_size*button_size + 1])
           counter_man = button_size*button_size+2
           nr_moves = 0
           t1 = clock()
           TimerMan.start()
       ok

func pTime()
        if flag_save = 0 or flag_move = 0
           warning()
        else
           counter_man++
           pPlaySleep()
           sleep(nr_sleep*1000)
           pElapsedTime()
           if counter_man = len(table1)
              TimerMan.stop()
           ok
        ok

func pPlaySleep
        see char(7)
        value = table1[counter_man]
        place = table2[counter_man]
        n_degree = number(table3[counter_man])
        btn_degree[empty][1] = value
        button[empty].setbuttoncolor("orange")
        button[empty] {settext(value)}
        n_degree = 0
        button[number(place)].setbuttoncolor("cyan")
        button[number(place)] {settext("")}
        empty = number(place)
        nr_moves = nr_moves + 1
        btnMoves.settext(string(nr_moves))

func pIncSleep
        nr_sleep = nr_sleep + 1
        sleepbtn.settext("Sleep Time: " + string(nr_sleep) + " s")

func pDecSleep
        if nr_sleep > 1
           nr_sleep = nr_sleep - 1
           sleepbtn.settext("Sleep Time: " + string(nr_sleep) + " s")
        ok

func sleep(x)
        nTime = x
        oTest = new qTest
        oTest.qsleep(nTime)
        return

func isGameOver
        flagend = 1
        for n=1 to button_size*button_size-1
              if button[n].text() != n or btn_degree[n][2] != 0
                 flagend = 0
                 exit
              ok
        next
        if flagend = 1
           new qmessagebox(win1) {
                   setwindowtitle("Game Over")
                   settext("Congratulations!")
                   show()
                   }
        ok

func rightPlace
        count = 0
        for n=1 to button_size*button_size
             if button[n].text() = n and btn_degree[n][2] = 0
                count = count + 1
             ok
        next
        rightbtn.settext("In the Right Place : " + count)

func warning
        new qmessagebox(win1) {
                setwindowtitle("Warning!")
                settext("First you must play and save the game.")
                show()
                }

func pElapsedTime
        t2 = (clock() - t1)/1000
        timebtn.settext("Elapsed Time : " + t2 + " s")

Class ButtonWithRotatedText

         oButton oLabel  cText="We are here"  n_degree = 30  nTransX = 50   nTransY = 0

func init( oParent)
        oButton = new qPushButton(oParent)
        oLabel  = new qLabel(oParent)
        oLabel.setAttribute(Qt_WA_TransparentForMouseEvents,True)
        oLabel.setAttribute(Qt_WA_DeleteOnClose, True)
        oButton.setAttribute(Qt_WA_DeleteOnClose, True)
        oButton.Show()
        return

func close()
        oLabel.close()
        oButton.close()
        return

func setstylesheet(x)
        oButton.setstylesheet(x)

func setgeometry( x,y,width,height)
        oButton.setgeometry(x,y,width,height)
        oLabel.setgeometry( x,y,width,height)

func setText( cValue)
        cText = cValue
        return

func Text()
         return cText

func setTranslate( x,y )
         nTransX = x
         nTransY = y
         return

func TranslateOffsetX()
        return nTransX

func TranslateOffsetY()
        return nTransY

func setRotation_degree( nValue)
        n_degree = nValue
        return

func Rotation_degree()
        return n_degree

func setClickEvent( cEvent)
        oButton.setClickEvent(cEvent)
        return

func braceend()
        draw()
        return

func setEnabled(value)
        oButton.setenabled(value)
        return

 func setButtonColor(color)
         colorIt = "background-color:" + color
         oButton.setstylesheet(colorIt)
         return

func draw()
         picture = new qpicture()
         color   = new qcolor() { setrgb(0,0,255,255) }
         pen     = new qpen()   { setcolor(color) setwidth(10) }

         painter = new qpainter()
         {
                       begin(picture)
                       setpen(pen)
                       oFont = new qfont("Courier New",12,75,0)
                       oFont.setpointsize(20)
                       setfont(oFont)
                       if n_degree = 0
                          if btn_degree[empty] [1]="In" p1 = -8 p2=0
                          translate(p1,p2) ok ok
                       if n_degree = 0
                          if btn_degree[empty] [1]<10 p1 = 10 p2=10 else p1=5 p2=10 ok
                          translate(p1,p2)
                       but n_degree = 90
                             if btn_degree[empty] [1]<10 p=-10 else p=-15 ok
                             translate(10,p)
                       but n_degree = 180
                              if btn_degree[empty] [1]<10 p1= 30 p2=-10 else p1=35 p2=-10 ok
                              translate(p1,p2)
                       but n_degree = 270
                             if btn_degree[empty] [1]<10 p=10 else p=15 ok
                             translate(30,p)
                       but n_degree = -90
                             if btn_degree[empty] [1]<10 p=10 else p=15 ok
                             translate(30,p)
                       but n_degree = -180
                              if btn_degree[empty] [1]<10 p1=30 p2=-10 else p1=35 p2=-10 ok
                              translate(p1,p2)
                       but n_degree = -270
                             if btn_degree[empty] [1]<10 p1=10 p2=-10 else p1=10 p2=-15 ok
                             translate(p1,p2)
                       ok
                       rotate(n_degree)
	          drawtext(0,0,this.Text())
                       endpaint()
         }
         oLabel {
                    setpicture(picture)
                     show()
                    }
         return


require 'io/console'

class Board
  SIZE = 4
  RANGE = 0...SIZE

  def initialize
    width = (SIZE*SIZE-1).to_s.size
    @frame = ("+" + "-"*(width+2)) * SIZE + "+"
    @form = "| %#{width}d " * SIZE + "|"
    @step = 0
    @orign = [*0...SIZE*SIZE].rotate.each_slice(SIZE).to_a.freeze
    @board = @orign.map{|row | row.dup}
    randomize
    draw
    message
    play
  end

  private

  def randomize
    @board[0][0], @board[SIZE-1][SIZE-1] = 0, 1
    @board[SIZE-1][0], @board[0][SIZE-1] = @board[0][SIZE-1], @board[SIZE-1][0]
    x, y, dx, dy = 0, 0, 1, 0
    50.times do
      nx,ny = [[x+dx,y+dy], [x+dy,y-dx], [x-dy,y+dx]]
                .select{|nx,ny| RANGE.include?(nx) and RANGE.include?(ny)}
                .sample
      @board[nx][ny], @board[x][y] = 0, @board[nx][ny]
      x, y, dx, dy = nx, ny, nx-x, ny-y
    end
    @x, @y = x, y
  end

  def draw
    puts "\e[H\e[2J"
    @board.each do |row|
      puts @frame
      puts (@form % row).sub(" 0 ", "   ")
    end
    puts @frame
    puts "Step: #{@step}"
  end

  DIR = {up: [-1,0], down: [1,0], left: [0,-1], right: [0,1]}
  def move(direction)
    dx, dy = DIR[direction]
    nx, ny = @x + dx, @y + dy
    if RANGE.include?(nx) and RANGE.include?(ny)
      @board[nx][ny], @board[@x][@y] = 0, @board[nx][ny]
      @x, @y = nx, ny
      @step += 1
      draw
    end
  end

  def play
    until @board == @orign
      case  key_in
      when "\e[A", "w" then move(:up)
      when "\e[B", "s" then move(:down)
      when "\e[C", "d" then move(:right)
      when "\e[D", "a" then move(:left)

      when "q","\u0003","\u0004"  then exit
      when "h"  then message
      end
    end

    puts "Congratulations, you have won!"
  end

  def key_in
    input = STDIN.getch
    if input == "\e"
      2.times {input << STDIN.getch}
    end
    input
  end

  def message
    puts <<~EOM
      Use the arrow-keys or WASD on your keyboard to push board in the given direction.
      PRESS q TO QUIT (or Ctrl-C or Ctrl-D)
    EOM
  end
end

Board.new


call SetCSS
' ---- fill 15 squares with 1 to 15
dim sq(16)
for i = 1 to 15: sq(i) = i: next

'----- shuffle the squares
[newGame]
for i = 1 to 100		' Shuffle the squares
	j	= rnd(0) * 16 + 1
	k	= rnd(0) * 16 + 1
	h	= sq(j)
	sq(j)	= sq(k)
	sq(k)	= h
next i

' ---- show the squares
[loop]
cls
html "

"
for i = 1 to 16
	html ""
	if sq(i) <> 0 then
		button	#pick, str$(sq(i)), [pick]
			#pick setkey(str$(i))
			#pick cssclass("lBtn")
	end if
	html "	"
	if i mod 4 = 0 then html "

"
next i
html "
"
wait

' ---- Find what square they picked
[pick]
picked	= val(EventKey$)
move	= 0             '                0000000001111111
if picked - 1 > 0 then  ' LEFT           1234567890123456
				if mid$(" *** *** *** ***",picked,1) = "*" and sq(picked -1) = 0 then move = -1 :end if
if picked + 1 < 17 then ' RIGHT
				if mid$("*** *** *** *** ",picked,1) = "*" and sq(picked +1) = 0 then move =  1 :end if
if picked - 4 > 0  then ' UP
				if mid$("    ************",picked,1) = "*" and sq(picked -4) = 0 then move = -4 :end if
if picked + 4 < 17 then ' DOWN
				if mid$("************    ",picked,1) = "*" and sq(picked +4) = 0 then move =  4 :end if
' ---- See if they picked a valid square next to the blank square
if move = 0 then
	print "Invalid move: ";sq(picked)
	wait
end if

' ---- Valid squire, switch it with the blank square
sq(picked + move) = sq(picked) ' move to the empty square
sq(picked) = 0
for i = 1 to 15  ' ---- If they got them all in a row they are a winner
	if sq(i) <> i then goto [loop]
next i

print "----- You are a winner -----"
input "Play again (Y/N)";a$
if a$ = "Y" then goto [newGame]		' set up new game
end

' ---- Make the squares look nice
SUB SetCSS
CSSClass ".lBtn", "{
background:wheat;border-width:5px;width:70px;
Text-Align:Center;Font-Size:24pt;Font-Weight:Bold;Font-Family:Arial;
}"
END SUB


extern crate rand;

use std::collections::HashMap;
use std::fmt;

use rand::Rng;
use rand::seq::SliceRandom;

#[derive(Copy, Clone, PartialEq, Debug)]
enum Cell {
    Card(usize),
    Empty,
}

#[derive(Eq, PartialEq, Hash, Debug)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

enum Action {
    Move(Direction),
    Quit,
}

type Board = [Cell; 16];
const EMPTY: Board = [Cell::Empty; 16];

struct P15 {
    board: Board,
}

impl P15 {
    fn new() -> Self {
        let mut board = EMPTY;
        for (i, cell) in board.iter_mut().enumerate().skip(1) {
            *cell = Cell::Card(i);
        }

        let mut rng = rand::thread_rng();

        board.shuffle(&mut rng);
        if !Self::is_valid(board) {
            // random swap
            let i = rng.gen_range(0, 16);
            let mut j = rng.gen_range(0, 16);
            while j == i {
                j = rng.gen_range(0, 16);
            }
            board.swap(i, j);
        }

        Self { board }
    }

    fn is_valid(mut board: Board) -> bool {
        // TODO: optimize
        let mut permutations = 0;

        let pos = board.iter().position(|&cell| cell == Cell::Empty).unwrap();

        if pos != 15 {
            board.swap(pos, 15);
            permutations += 1;
        }

        for i in 1..16 {
            let pos = board
                .iter()
                .position(|&cell| match cell {
                    Cell::Card(value) if value == i => true,
                    _ => false,
                })
                .unwrap();

            if pos + 1 != i {
                board.swap(pos, i - 1);
                permutations += 1;
            }
        }

        permutations % 2 == 0
    }

    fn get_empty_position(&self) -> usize {
        self.board.iter().position(|&c| c == Cell::Empty).unwrap()
    }

    fn get_moves(&self) -> HashMap<Direction, Cell> {
        let mut moves = HashMap::new();
        let i = self.get_empty_position();

        if i > 3 {
            moves.insert(Direction::Up, self.board[i - 4]);
        }
        if i % 4 != 0 {
            moves.insert(Direction::Left, self.board[i - 1]);
        }
        if i < 12 {
            moves.insert(Direction::Down, self.board[i + 4]);
        }
        if i % 4 != 3 {
            moves.insert(Direction::Right, self.board[i + 1]);
        }
        moves
    }

    fn play(&mut self, direction: &Direction) {
        let i = self.get_empty_position();
        // This is safe because `ask_action` only returns legal moves
        match *direction {
            Direction::Up => self.board.swap(i, i - 4),
            Direction::Left => self.board.swap(i, i - 1),
            Direction::Right => self.board.swap(i, i + 1),
            Direction::Down => self.board.swap(i, i + 4),
        };
    }

    fn is_complete(&self) -> bool {
        self.board.iter().enumerate().all(|(i, &cell)| match cell {
            Cell::Card(value) => value == i + 1,
            Cell::Empty => i == 15,
        })
    }
}

impl fmt::Display for P15 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        r#try!(write!(f, "+----+----+----+----+\n"));
        for (i, &cell) in self.board.iter().enumerate() {
            match cell {
                Cell::Card(value) => r#try!(write!(f, "| {:2} ", value)),
                Cell::Empty => r#try!(write!(f, "|    ")),
            }

            if i % 4 == 3 {
                r#try!(write!(f, "|\n"));
                r#try!(write!(f, "+----+----+----+----+\n"));
            }
        }
        Ok(())
    }
}

fn main() {
    let mut p15 = P15::new();

    for turns in 1.. {
        println!("{}", p15);
        match ask_action(&p15.get_moves()) {
            Action::Move(direction) => {
                p15.play(&direction);
            }
            Action::Quit => {
                println!("Bye !");
                break;
            }
        }

        if p15.is_complete() {
            println!("Well done ! You won in {} turns", turns);
            break;
        }
    }
}

fn ask_action(moves: &HashMap<Direction, Cell>) -> Action {
    use std::io::{self, Write};
    use Action::*;
    use Direction::*;

    println!("Possible moves:");

    if let Some(&Cell::Card(value)) = moves.get(&Up) {
        println!("\tU) {}", value);
    }
    if let Some(&Cell::Card(value)) = moves.get(&Left) {
        println!("\tL) {}", value);
    }
    if let Some(&Cell::Card(value)) = moves.get(&Right) {
        println!("\tR) {}", value);
    }
    if let Some(&Cell::Card(value)) = moves.get(&Down) {
        println!("\tD) {}", value);
    }
    println!("\tQ) Quit");
    print!("Choose your move : ");
    io::stdout().flush().unwrap();

    let mut action = String::new();
    io::stdin().read_line(&mut action).expect("read error");
    match action.to_uppercase().trim() {
        "U" if moves.contains_key(&Up) => Move(Up),
        "L" if moves.contains_key(&Left) => Move(Left),
        "R" if moves.contains_key(&Right) => Move(Right),
        "D" if moves.contains_key(&Down) => Move(Down),
        "Q" => Quit,
        _ => {
            println!("Unknown action: {}", action);
            ask_action(moves)
        }
    }
}


import java.util.Random

import jline.console._

import scala.annotation.tailrec
import scala.collection.immutable
import scala.collection.parallel.immutable.ParVector

object FifteenPuzzle {
  def main(args: Array[String]): Unit = play()

  @tailrec def play(len: Int = 1000): Unit = if(gameLoop(Board.randState(len))) play(len)
  def gameLoop(board: Board): Boolean = {
    val read = new ConsoleReader()
    val km = KeyMap.keyMaps().get("vi-insert")
    val opMap = immutable.HashMap[Operation, Char](
      Operation.PREVIOUS_HISTORY -> 'u',
      Operation.BACKWARD_CHAR -> 'l',
      Operation.NEXT_HISTORY -> 'd',
      Operation.FORWARD_CHAR -> 'r')

    @tailrec
    def gloop(b: Board): Boolean = {
      println(s"\u001B[2J\u001B[2;0H$b\n←↑→↓q")
      if(b.isSolved) println("Solved!\nPlay again? (y/n)")

      read.readBinding(km) match{
        case Operation.SELF_INSERT => read.getLastBinding match{
          case "q" => false
          case "y" if b.isSolved => true
          case "n" if b.isSolved => false
          case _ => gloop(b)
        }
        case op: Operation if opMap.isDefinedAt(op) => gloop(b.move(opMap(op)))
        case _ => gloop(b)
      }
    }

    gloop(board)
  }

  case class Board(mat: immutable.HashMap[(Int, Int), Int], x: Int, y: Int) {
    def move(mvs: Seq[Char]): Board = mvs.foldLeft(this){case (b, m) => b.move(m)}
    def move(mov: Char): Board = mov match {
      case 'r' if x < 3 => Board(mat ++ Seq(((x, y), mat((x + 1, y))), ((x + 1, y), 0)), x + 1, y)
      case 'l' if x > 0 => Board(mat ++ Seq(((x, y), mat((x - 1, y))), ((x - 1, y), 0)), x - 1, y)
      case 'd' if y < 3 => Board(mat ++ Seq(((x, y), mat((x, y + 1))), ((x, y + 1), 0)), x, y + 1)
      case 'u' if y > 0 => Board(mat ++ Seq(((x, y), mat((x, y - 1))), ((x, y - 1), 0)), x, y - 1)
      case _ => this
    }

    def isSolved: Boolean = sumDist == 0
    def sumDist: Int = mat.to(LazyList).map{ case ((a, b), n) => if(n == 0) 6 - a - b else (a + b - ((n - 1) % 4) - ((n - 1) / 4)).abs }.sum

    override def toString: String = {
      val lst = mat.toVector.map { case ((a, b), n) => (4 * b + a, n) }.sortWith(_._1 < _._1).map(_._2)
      lst.map { n => if (n == 0) "  " else f"$n%2d" }.grouped(4).map(_.mkString(" ")).mkString("\n")
    }
  }

  object Board {
    val moves: Vector[Char] = Vector('r', 'l', 'd', 'u')

    def apply(config: Vector[Int]): Board = {
      val ind = config.indexOf(0)
      val formed = config.zipWithIndex.map { case (n, i) => ((i % 4, i / 4), n) }
      val builder = immutable.HashMap.newBuilder[(Int, Int), Int]
      builder ++= formed
      Board(builder.result, ind % 4, ind / 4)
    }

    def solveState: Board = apply((1 to 15).toVector :+ 0)
    def randState(len: Int, rand: Random = new Random()): Board = Iterator
      .fill(len)(moves(rand.nextInt(4)))
      .foldLeft(Board.solveState) { case (state, mv) => state.move(mv) }
  }
}


(import (scheme base)
        (scheme read)
        (scheme write)
        (srfi 27))    ; random numbers

(define *start-position* #(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #\space))
(random-source-randomize! default-random-source)

;; return a 16-place vector with the tiles randomly shuffled
(define (create-start-position)
  (let ((board (vector-copy *start-position*)))
    (do ((i 0 (+ 1 i))
         (moves (find-moves board) (find-moves board)))
      ((and (>= i 100)
            (not (finished? board)))
       board)
      (make-move board
                 (list-ref moves (random-integer (length moves)))))))

;; return index of space
(define (find-space board)
  (do ((i 0 (+ 1 i)))
    ((equal? #\space (vector-ref board i)) i)))

;; return a list of symbols indicating available moves
(define (find-moves board)
  (let* ((posn (find-space board))
         (row (quotient posn 4))
         (col (remainder posn 4))
         (result '()))
    (when (> row 0) (set! result (cons 'up result)))
    (when (< row 3) (set! result (cons 'down result)))
    (when (> col 0) (set! result (cons 'left result)))
    (when (< col 3) (set! result (cons 'right result)))
    result))

;; make given move - assume it is legal
(define (make-move board move)
  (define (swap posn-1 posn-2)
    (let ((tmp (vector-ref board posn-1)))
      (vector-set! board posn-1 (vector-ref board posn-2))
      (vector-set! board posn-2 tmp)))
  ;
  (let ((posn (find-space board)))
    (case move
      ((left)
       (swap posn (- posn 1)))
      ((right)
       (swap posn (+ posn 1)))
      ((up)
       (swap posn (- posn 4)))
      ((down)
       (swap posn (+ posn 4))))))

(define (finished? board)
  (equal? board *start-position*))

(define (display-board board)
  (do ((i 0 (+ 1 i)))
    ((= i (vector-length board)) (newline))
    (when (zero? (modulo i 4)) (newline))
    (let ((curr (vector-ref board i)))
      (display curr)
      (display (if (and (number? curr)
                        (> curr 9))
                 " "
                 "  ")))))

;; the main game loop
(define (play-game)
  (let ((board (create-start-position)))
    (do ((count 1 (+ count 1))
         (moves (find-moves board) (find-moves board)))
      ((finished? board)
       (display (string-append "\nCOMPLETED PUZZLE in "
                               (number->string count)
                               " moves\n")))
      (display-board board)
      (display "Enter a move: ") (display moves) (newline)
      (let ((move (read)))
        (if (memq move moves)
          (make-move board move)
          (display "Invalid move - try again"))))))

(play-game)


tiles=[1:15,0];
solution=[tiles(1:4);...
          tiles(5:8);...
          tiles(9:12);...
          tiles(13:16)];
solution=string(solution);
solution(16)=" ";

init_pos=grand(1,"prm",tiles);
puzzle=[init_pos(1:4);...
        init_pos(5:8);...
        init_pos(9:12);...
        init_pos(13:16)];
puzzle=string(puzzle);

blank_pos=[];
for i=1:4
    for j=1:4
        if puzzle(i,j)=="0" then
            blank_pos=[i,j];
        end
    end
end

clear i j

puzzle(blank_pos(1),blank_pos(2))=" ";

n_moves=0;
solved=%F;
while ~solved
    disp(puzzle); mprintf("\n");

    neighbours=[0 -1;...
               -1  0;...
                0 +1;...
               +1  0];
    neighbours(:,1)=neighbours(:,1)+blank_pos(1);
    neighbours(:,2)=neighbours(:,2)+blank_pos(2);
    neighbours=[neighbours zeros(4,1)]

    i=0;
    for i=1:4
        if ~(neighbours(i,1)<1 | neighbours(i,1)>4 |...
           neighbours(i,2)<1 | neighbours(i,2)>4) then
           neighbours(i,3)=evstr(puzzle(neighbours(i,1),neighbours(i,2)));
       end
    end

    valid_move=%F;
    while ~valid_move
        move_tile=[];
        move_tile=input("Enter tile you want to move (0 to exit):");
        if sum(move_tile==neighbours(:,3)) & move_tile~=0 then
            valid_move=%T;
            n_moves=n_moves+1;
        elseif move_tile==0 then
            disp("Exit");
            abort
        else
            disp(puzzle);
            disp("Invalid input");
        end
    end

    neighb_i=find(neighbours(:,3)'==move_tile);
    puzzle(neighbours(neighb_i,1),neighbours(neighb_i,2))=" ";
    puzzle(blank_pos(1),blank_pos(2))=string(move_tile);
    blank_pos=neighbours(neighb_i,1:2);

    if sum(puzzle==solution)==16 then
        solved=%T;
        disp(puzzle);
        mprintf("\n"+...
                "   _____       _               _ _ \n"+...
                "  / ____|     | |             | | |\n"+...
                " | (___   ___ | |_   _____  __| | |\n"+...
                "  \\___ \\ / _ \\| \\ \\ / / _ \\/ _` | |\n"+...
                "  ____) | (_) | |\\ V /  __/ (_| |_|\n"+...
                " |_____/ \\___/|_| \\_/ \\___|\\__,_(_)\n")
    end
end

disp("Solved in "+string(n_moves)+" moves.");


BEGIN
    CLASS FIFTEENPUZZLE(NUMTILES, SIDE, WIDTH, SEED);
        INTEGER NUMTILES, SIDE, WIDTH, SEED;
    BEGIN
        INTEGER ARRAY TILES(0:NUMTILES);
        INTEGER BLANKPOS;

        PROCEDURE INVARIANT;
        BEGIN
            INTEGER ARRAY UQ(0:NUMTILES);
            INTEGER I;
            FOR I := 0 STEP 1 UNTIL NUMTILES DO UQ(I) := -1;
            FOR I := 0 STEP 1 UNTIL NUMTILES DO
            BEGIN
                INTEGER T;
                T := TILES(I);
                IF UQ(T) <> -1 THEN ERROR("TILES ARE NOT UNIQUE");
                UQ(T) := T;
            END;
            IF TILES(BLANKPOS) <> 0 THEN ERROR("BLANKPOS IS NOT BLANK");
        END;

        PROCEDURE SHUFFLE;
        BEGIN
            BOOLEAN B;
            WHILE NOT B DO
            BEGIN
                INTEGER N;
                RESET;
                ! DON'T INCLUDE THE BLANK SPACE IN THE SHUFFLE, LEAVE IT
                ! IN THE HOME POSITION ;
                N := NUMTILES;
                WHILE N > 1 DO
                BEGIN
                    INTEGER R, TMP;
                    R := UNIFORM(0, N, SEED); N := N - 1;
                    TMP := TILES(R);
                    TILES(R) := TILES(N);
                    TILES(N) := TMP;
                END;
                B := ISSOLVABLE;
            END;
            INVARIANT;
        END;

        PROCEDURE RESET;
        BEGIN
            INTEGER I;
            FOR I := 0 STEP 1 UNTIL NUMTILES DO
                TILES(I) := MOD((I + 1), NUMTILES + 1);
            BLANKPOS := NUMTILES;
            INVARIANT;
        END;

        ! 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
        ;

        BOOLEAN PROCEDURE ISSOLVABLE;
        BEGIN
            INTEGER COUNTINVERSIONS;
            INTEGER I, J;
            FOR I := 0 STEP 1 UNTIL NUMTILES - 1 DO
                FOR J := 0 STEP 1 UNTIL I - 1 DO
                    IF TILES(J) > TILES(I) THEN
                        COUNTINVERSIONS := COUNTINVERSIONS + 1;
            ISSOLVABLE := MOD(COUNTINVERSIONS, 2) = 0;
        END;

        PROCEDURE PRINTBOARD;
        BEGIN
            INTEGER I, J;

            PROCEDURE PRINTLINE;
            BEGIN
              INTEGER ROW, COL;
              !      +-----+-----+-----+-----+ ;
              FOR ROW := 1 STEP 1 UNTIL SIDE DO
              BEGIN
                  OUTCHAR('+');
                  FOR COL := 0 STEP 1 UNTIL WIDTH DO OUTCHAR('-');
              END;
              OUTCHAR('+');
              OUTIMAGE;
            END;

            PROCEDURE PRINTCELL(T); INTEGER T;
            BEGIN
                IF T = 0 THEN
                BEGIN
                    INTEGER R;
                    FOR R := 1 STEP 1 UNTIL WIDTH DO
                        OUTCHAR(' ');
                END
                ELSE OUTINT(T, WIDTH);
                OUTCHAR(' ');
            END;

            !      +-----+-----+-----+-----+
            !      |  1  |  2  |  3  |  4  |
            !      +-----+-----+-----+-----+
            !      |  5  |  6  |  7  |  8  |
            !      +-----+-----+-----+-----+
            !      |  9  |  10 |  11 |     |
            !      +-----+-----+-----+-----+
            !      |  13 |  14 |  15 |  12 |
            !      +-----+-----+-----+-----+   ;

            FOR I := 1 STEP 1 UNTIL SIDE DO
            BEGIN
                PRINTLINE;
                OUTCHAR('|');
                FOR J := 1 STEP 1 UNTIL SIDE DO
                BEGIN
                    INTEGER T;
                    T := TILES((I - 1) * SIDE + (J - 1));
                    PRINTCELL(T);
                    OUTCHAR('|');
                END;
                OUTIMAGE;
            END;
            PRINTLINE;
        END;

        BOOLEAN PROCEDURE DONE;
        BEGIN
            BOOLEAN ORDERED;
            INTEGER I, EXPECT;
            ORDERED := TRUE;
            EXPECT := 1;
            FOR I := 0 STEP 1 UNTIL NUMTILES - 1 DO
            BEGIN
                IF I <> BLANKPOS THEN
                BEGIN
                    IF TILES(I) <> EXPECT THEN
                        ORDERED := FALSE;
                    EXPECT := EXPECT + 1;
                END;
            END;
            DONE := ORDERED;
        END;

        PROCEDURE REQUEST;
        BEGIN
            INTEGER ARRAY CANDIDATES(1:4);
            INTEGER I, CANDCOUNT, CHOOSE;
            BOOLEAN VALIDINPUT;

            PROCEDURE ADDCAND(IDX); INTEGER IDX;
            BEGIN
                IF IDX >= 0 AND IDX <= NUMTILES THEN
                BEGIN
                    CANDCOUNT := CANDCOUNT + 1;
                    CANDIDATES(CANDCOUNT) := TILES(IDX);
                END;
            END;

            PRINTBOARD;

            IF BLANKPOS <= NUMTILES - SIDE     THEN ADDCAND(BLANKPOS + SIDE);
            IF BLANKPOS >= SIDE                THEN ADDCAND(BLANKPOS - SIDE);
            IF MOD(BLANKPOS, SIDE) <> SIDE - 1 THEN ADDCAND(BLANKPOS + 1);
            IF MOD(BLANKPOS, SIDE) <> 0        THEN ADDCAND(BLANKPOS - 1);

            WHILE NOT VALIDINPUT DO
            BEGIN
                OUTTEXT("YOUR MOVE: ");

                FOR I := 1 STEP 1 UNTIL CANDCOUNT DO
                    OUTINT(CANDIDATES(I), SIDE);
                OUTIMAGE;
                CHOOSE := ININT;

                FOR I := 1 STEP 1 UNTIL CANDCOUNT DO
                    IF CHOOSE = CANDIDATES(I) THEN
                    BEGIN
                        INTEGER LOOKUP;
                        FOR LOOKUP := 0 STEP 1 UNTIL NUMTILES DO
                            IF NOT VALIDINPUT AND TILES(LOOKUP) = CHOOSE THEN
                            BEGIN
                                TILES(BLANKPOS) := TILES(LOOKUP);
                                TILES(LOOKUP) := 0;
                                BLANKPOS := LOOKUP;
                                INVARIANT;
                                VALIDINPUT := TRUE;
                            END;
                    END;

                IF NOT VALIDINPUT THEN
                BEGIN
                    OUTTEXT("INVALID INPUT!");
                    OUTIMAGE;
                END;
            END;
        END;

        SHUFFLE;
    END;

    REF(FIFTEENPUZZLE) P;

    OUTTEXT("INPUT RANDOM SEED: ");
    OUTIMAGE;
    P :- NEW FIFTEENPUZZLE(15, 4, 3, ININT); ! ININT = RANDOM SEED ;
    WHILE NOT P.DONE DO
        P.REQUEST;
    P.PRINTBOARD;
END.


(* Load required Modules for Moscow ML *)
load "Int";
load "Random";

(* Mutable Matrix *)
signature MATRIX =
sig
	type 'a matrix
	val construct : 'a -> int * int -> 'a matrix
	val size : 'a matrix -> int * int
	val get : 'a matrix -> int * int -> 'a
	val set : 'a matrix -> int * int -> 'a -> unit
end

structure Matrix :> MATRIX =
struct
	(* Array of rows, where the rows are a array of 'a *)
	type 'a matrix = 'a Array.array Array.array

	fun 'a construct (a : 'a) (width, height) : 'a matrix =
		if width < 1 orelse height < 1
			then raise Subscript
			else Array.tabulate (height, fn _ => Array.tabulate (width, fn _ => a))

	fun size b =
		let
			val firstrow = Array.sub (b, 0)
		in
			(Array.length firstrow, Array.length b)
		end

	fun get b (x, y) = Array.sub (Array.sub (b, y), x)

	fun set b (x, y) v = Array.update (Array.sub (b, y), x, v)
end

signature P15BOARD =
sig
	type board
	datatype direction = North | East | South | West

	val construct : int * int -> board
	val emptyField : board -> int * int
	val get : board -> int * int -> int option
	val size : board -> int * int

	exception IllegalMove
	val moves : board -> int list
	val move : board -> int -> unit

	val issolved : board -> bool
end

(* Game Logic and data *)

structure Board :> P15BOARD =
struct
	(* Matrix + empty Field position *)
	type board = int option Matrix.matrix * (int * int) ref

	datatype direction = North | East | South | West

	exception IllegalMove

	fun numberat width (x, y) = (y*width + x + 1)

	fun construct (width, height) =
		let
			val emptyBoard : int option Matrix.matrix = Matrix.construct NONE (width, height)
		in
			(* Fill the board with numbers *)
			List.tabulate (height, fn y => List.tabulate (width, fn x =>
					Matrix.set emptyBoard (x, y) (SOME (numberat width (x, y)))));
			(* Clear the last field *)
			Matrix.set emptyBoard (width-1, height-1) NONE;
			(* Return the board *)
			(emptyBoard, ref (width-1, height-1))
		end

	fun emptyField (_, rfield) = !rfield

	fun get (mat, _) (x, y) = Matrix.get mat (x, y)

	fun size (mat, _) = Matrix.size mat

	(* toggle the empty field with a given field *)
	fun toggle (mat, rpos) pos =
		let
			val pos' = !rpos
			val value = Matrix.get mat pos
		in
			Matrix.set mat pos NONE;
			Matrix.set mat pos' value;
			rpos := pos
		end

	(* Get list of positions of the neighbors of a given field *)
	fun neighbors mat (x, y) : (int * int) list =
		let
			val (width, height) = Matrix.size mat
			val directions = [(x, y-1), (x+1, y), (x, y+1), (x-1, y)]
		in
			List.mapPartial (fn pos => SOME (Matrix.get mat pos; pos) handle Subscript => NONE) directions
		end

	fun moves (mat, rpos) =
		let
			val neighbors = neighbors mat (!rpos)
		in
			map (fn pos => valOf (Matrix.get mat pos)) neighbors
		end

	fun move (mat, rpos) m =
		let
			val (hx, hy) = !rpos
			val neighbors = neighbors mat (hx, hy)
			val optNeighbor = List.find (fn pos => SOME m = Matrix.get mat pos) neighbors
		in
			if isSome optNeighbor
			then
				toggle (mat, rpos) (valOf optNeighbor)
			else
				raise IllegalMove
		end

	fun issolved board =
		let
			val (width, height) = size board
			val xs = List.tabulate (width,  fn x => x)
			val ys = List.tabulate (height, fn y => y)
		in
			List.all (fn x => List.all (fn y => (x + 1 = width andalso y + 1 = height) orelse get board (x, y) = SOME (numberat width (x,y))) ys) xs
		end
end

(* Board Shuffle *)
signature BOARDSHUFFLE =
sig
	val shuffle : Board.board -> int -> unit
end

structure Shuffle :> BOARDSHUFFLE =
struct
	(*
	 * Note: Random Number Interfaces are different in SML/NJ and Moscow ML. Comment out the corresponding version:
	 *)

	(*
	(* SML/NJ - Version *)
	val time = Time.now ()
	val timeInf = Time.toMicroseconds time
	val timens = Int.fromLarge (LargeInt.mod (timeInf, 1073741823));
	val rand = Random.rand (timens, timens)

	fun next n = Random.randRange (0, n) rand
	*)

	(* Moscow ML - Version *)
	val generator = Random.newgen ()
	fun next n = Random.range (0, n) generator

	fun shuffle board 0 = if (Board.issolved board) then shuffle board 1 else ()
	  | shuffle board n =
	  	let
			val moves = Board.moves board
			val move  = List.nth (moves, next (List.length moves - 1))
		in
			Board.move board move;
			shuffle board (n-1)
		end
end

(* Console interface *)

signature CONSOLEINTERFACE =
sig
	val start : unit -> unit
	val printBoard : Board.board -> unit
end

structure Console :> CONSOLEINTERFACE =
struct
	fun cls () = print "\^[[1;1H\^[[2J"

	fun promptNumber prompt =
		let
			val () = print prompt
			(* Input + "\n" *)
			val line   = valOf (TextIO.inputLine TextIO.stdIn)
			val length = String.size line
			val input  = String.substring (line, 0, length - 1)
			val optnum = Int.fromString input
		in
			if isSome optnum
				then valOf optnum
				else (print "Input is not a number.\n"; promptNumber prompt)
		end

	fun fieldToString (SOME x) = Int.toString x
	  | fieldToString (NONE  ) = ""

	fun boardToString board =
		let
			val (width, height) = Board.size board
			val xs = List.tabulate (width,  fn x => x)
			val ys = List.tabulate (height, fn y => y)
		in
			foldl (fn (y, str) => (foldl (fn (x, str') => str' ^ (fieldToString (Board.get board (x, y))) ^ "\t") str xs) ^ "\n") "" ys
		end

	fun printBoard board = print (boardToString board)

	fun loop board =
		let
			val rvalidInput = ref false
			val rinput      = ref 42
			val () = cls ()
			val () = printBoard board
		in
			(* Ask for a move and repeat until it is a valid move *)
			while (not (!rvalidInput)) do
				(
					rinput := promptNumber "Input the number to move: ";
					Board.move board (!rinput);
					rvalidInput := true
				) handle Board.IllegalMove => print "Illegal move!\n"
		end

	fun start () =
		let
			val () = cls ()
			val () = print "Welcome to nxm-Puzzle!\n"
			val (width, height) = (promptNumber "Enter the width: ", promptNumber "Enter the height: ")
			val diff = (promptNumber "Enter the difficulty (number of shuffles): ")
			val board = Board.construct (width, height)
		in
			Shuffle.shuffle board diff;
			while (not (Board.issolved board)) do loop board;
			print "Solved!\n"
		end
end

val () = Console.start()


 # 15puzzle_21.tcl - HaJo Gurt -  2016-02-16
 # http://wiki.tcl.tk/14403

 #: 15-Puzzle - with grid, buttons and colors

  package require Tk

  set progVersion "15-Puzzle v0.21";        # 2016-02-20

  global Msg Moves PuzzNr GoalNr
  set Msg    " "
  set Moves  -1
  set PuzzNr  0
  set GoalNr  0

  set Keys   { 11 12 13 14  21 22 23 24  31 32 33 34  41 42 43 44 }

  set Puzz_T {  T  h  e  F   i  f  t  e   e  n  P  u   z  z  l  e }; # Title
  set Goal_T {  x  x  x  F   i  f  t  e   e  n  x  x   x  x  x  x }; # Title-highlight

  set Puzz_0 {  E  G  P  N   C  A  F  B   D  L  H  I   O  K  M  _ }; # -  / 116
  set Puzz_1 {  C  A  F  B   E  G  P  N   D  L  H  I   O  K  M  _ }; # E  / 156 from Tk-demo
  set Puzz_2 {  E  O  N  K   M  I  _  G   B  H  L  P   C  F  A  D }; # L  / 139
  set Puzz_3 {  P  G  M  _   E  L  N  D   O  K  H  I   B  C  F  A }; # EK / 146

  set Goal_0 {  A  B  C  D   E  F  G  H   I  K  L  M   N  O  P  _ }; # Rows LTR   / 1:E : 108
  set Goal_1 {  A  E  I  N   B  F  K  O   C  G  L  P   D  H  M  _ }; # Cols forw. / 1:M : 114

  set Puzz $Puzz_T
  set Goal $Goal_T

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+---

  proc Move {k} {
  # find the key with the empty tile:
    set e -1
    foreach p $::Keys  {
      set t [.key$p cget -text]
      if { $t eq "_" } { set e $p }
    }
    if {$e  <  0} {return 0};   # no key with empty tile found
    if {$k == $e} {return 0};   # click was on the empty tile

    set t [.key$k cget -text]
    .key$e config -text $t
    .key$k config -text "_";
    return 1
  }

  proc Check {} {
    set ok 0
    set i  0
    foreach k $::Keys {
      set t [.key$k cget -text]
      set g [lindex $::Goal $i]
      incr i

      .key$k config -background white
      if { $t eq $g  } { .key$k config -background lightgreen; incr ok }
      if { $t eq "_" } { .key$k config -background gray }
    }

    # Solved:
    update
    if { $ok > 15 && $::Moves > 0} {
      foreach k $::Keys  {
        .key$k flash; bell;
      }
    }
  }

  proc Click {k} {
    set ::Msg ""
    set val [.key$k cget -text]
    set ok [Move $k]

    incr ::Moves $ok
    wm title . "$::Moves moves"
    Check
  }

  proc ShowKeys {} {
    set i 0
    foreach k $::Keys  {
      set t [lindex $::Puzz $i]
      incr i
      .key$k config -text $t -background white;
    }
    Check
  }

  proc NewGame {N} {
    global Msg Moves PuzzNr GoalNr

    incr  PuzzNr $N
    if { $PuzzNr > 3} { set PuzzNr 0 }

                         set ::Goal $::Goal_0;
    if { $GoalNr == 1} { set ::Goal $::Goal_1; }

    if { $PuzzNr == 0} { set ::Puzz $::Puzz_0; }
    if { $PuzzNr == 1} { set ::Puzz $::Puzz_1; }
    if { $PuzzNr == 2} { set ::Puzz $::Puzz_2; }
    if { $PuzzNr == 3} { set ::Puzz $::Puzz_3; }

                  set Msg "Try again"
    if { $N>0 } { set Msg "New game" }

    set Moves 0
    ShowKeys
    wm title . "$Msg "
  }

#---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+---

  button .reset   -text "Restart"  -fg blue -command {NewGame  0}
  button .newGame -text "New Game" -fg red  -command {NewGame +1}

  foreach k $::Keys {
    button .key$k -text "$k" -width 4 -command "Click $k"
  }

  grid .newGame x .reset x -sticky nsew

  grid .key11 .key12 .key13 .key14  -sticky nsew  -padx 2 -pady 2
  grid .key21 .key22 .key23 .key24  -sticky nsew  -padx 2 -pady 2
  grid .key31 .key32 .key33 .key34  -sticky nsew  -padx 2 -pady 2
  grid .key41 .key42 .key43 .key44  -sticky nsew  -padx 2 -pady 2

  grid configure .newGame .reset  -columnspan 2 -padx 4

  ShowKeys; Check
  wm title . $progVersion
  focus -force .
  wm resizable . 0 0

# For some more versions, see:  http://wiki.tcl.tk/15067 : Classic 15 Puzzle and http://wiki.tcl.tk/15085 : N-Puzzle


#!/usr/bin/env bash
main() {
  local puzzle=({1..15} " ") blank moves_kv i key count total last
  printf '\n'
  show_puzzle "${puzzle[@]}"
  printf '\nPress return to scramble.'
  read _
  IFS= readarray -t puzzle < <(scramble "${puzzle[@]}")
  printf '\r\e[A\e[A\e[A\e[A\e[A\e[A'
  show_puzzle "${puzzle[@]}"
  printf '\nUse hjkl to slide tiles. '
  printf '\r\e[A\e[A\e[A\e[A\e[A'
  total=0
  while ! solved "${puzzle[@]}"; do
    show_puzzle "${puzzle[@]}"
    { read blank; readarray -t moves_kv; } < <(find_moves "${puzzle[@]}")
    local count=${#moves_kv[@]}/2
    local -A moves
    for (( i=0; i<count; i++ )); do
      moves[${moves_kv[i]}]=${moves_kv[i+count]}
    done
    read -r -n 1 key
    printf '\r '
    if [[ "$key" = u ]]; then
      (( total -= 2 ))
      case "$last" in
        h) key=l;;
        j) key=k;;
        k) key=j;;
        l) key=h;;
      esac
    fi
    if [[ -n "${moves[$key]}" ]]; then
      last=$key
      (( total++ ))
      i=${moves[$key]}
      puzzle[$blank]=${puzzle[i]}
      puzzle[i]=' '
    fi
    printf '\r\e[A\e[A\e[A\e[A'
  done
  show_puzzle "${puzzle[@]}"
  printf '\nSolved in %d moves.     \n' "$total"
}

solved() {
  local solved=({1..15} ' ')
  [[ "${puzzle[*]}" == "${solved[*]}" ]]
}

show_puzzle() {
  printf '%2s %2s %2s %2s\n' "[email protected]"
}

find_moves() {
  local puzzle=("[email protected]")
  local i j blank
  for (( i=0; i<${#puzzle[@]}; ++i )); do
    if [[ "${puzzle[i]}" == " " ]]; then
      blank=$i
      break
    fi
  done
  local -A moves=()
  if (( blank%4 )); then
    # swapping blank with tile to its left
    # is sliding that tile right, which is the l key
    moves['l']=$(( blank-1 ))
  fi
  if (( blank%4 != 3 )); then
    moves['h']=$(( blank+1 )) # left
  fi
  if (( blank >= 4 )); then
    moves['j']=$(( blank-4 )) # down
  fi
  if (( blank < 12 )); then
    moves['k']=$(( blank+4 )) # up
  fi
  printf '%s\n' "$blank" "${!moves[@]}" "${moves[@]}"
}

scramble() {
  local puzzle=("[email protected]") i j
  for (( i=0; i<256; ++i )); do
    local blank moves
    { read blank; readarray -t moves; } < <(find_moves "${puzzle[@]}")
    moves=(${moves[@]:${#moves[@]}/2})
    local dir=$(( RANDOM % ${#moves[@]} ))
    j=${moves[dir]}
    puzzle[blank]=${puzzle[j]}
    puzzle[j]=' '
  done
  printf '%s\n' "${puzzle[@]}"
}

main "[email protected]"


Public iSide As Integer
Public iSize As Integer
Public iGrid() As Integer
Public lMoves As Long
Public sMessage As String
Public Const sTitle As String = "Tile Puzzle"

Sub PlayGame()
    Dim iNum As Integer
    Dim i As Integer
    Dim vInput As Variant

DefineGrid:
    vInput = InputBox("Enter size of grid, as a whole number" & String(2, vbCr) & "(e.g. for a 4 x 4 grid, enter '4')", sTitle, 4)
    If vInput = "" Then Exit Sub
    If Not IsNumeric(vInput) Then GoTo DefineGrid
    iSide = vInput
    If iSide < 3 Or iNum > 10 Then GoTo DefineGrid
    iSize = iSide ^ 2
    ReDim iGrid(1 To iSize)

Initalize:
    InitializeGrid
    If Not IsSolvable Then GoTo Initalize

GetInput:
    vInput = InputBox(ShowGrid & vbCr & "Enter number to move into empty tile", sTitle)
    If vInput = "" Then
        If MsgBox("Are you sure? This will end the current game.", vbExclamation + vbYesNo, sTitle) = vbYes Then Exit Sub
    End If
    If Not IsNumeric(vInput) Then
        sMessage = "'" & vInput & "' is not a valid tile"
        GoTo GetInput
    End If
    iNum = vInput
    If iNum < 1 Or iNum > iSize - 1 Then
        sMessage = iNum & " is not a valid tile"
        GoTo GetInput
    End If
    i = FindTile(iNum)
    If Not ValidMove(i) Then GoTo GetInput
    MoveTile (i)
    If TestGrid Then
        MsgBox "SUCCESS! You solved the puzzle in " & lMoves & " moves", vbInformation + vbOKOnly, sTitle
    Else
        GoTo GetInput
    End If
End Sub

Function RandomTile() As Integer
    Randomize
    RandomTile = Int(Rnd * iSize) + 1
End Function

Function GetX(ByVal i As Integer) As Integer
    GetX = Int((i - 1) / iSide) + 1
End Function

Function GetY(ByVal i As Integer) As Integer
    GetY = (i - 1) Mod iSide + 1
End Function

Function GetI(ByVal x As Integer, y As Integer)
    GetI = (x - 1) * iSide + y
End Function

Function InitializeGrid()
    Dim i As Integer
    Dim x As Integer
    Dim y As Integer

    sMessage = "New " & iSide & " x " & iSide & " game started" & vbCr

    For i = 1 To iSize
        iGrid(i) = 0
    Next i
    For i = 1 To iSize - 1
        Do
            x = RandomTile
            If iGrid(x) = 0 Then iGrid(x) = i
        Loop Until iGrid(x) = i
    Next i
    lMoves = 0
End Function

Function IsSolvable() As Boolean
    Dim i As Integer
    Dim j As Integer
    Dim iCount As Integer
    For i = 1 To iSize - 1
        For j = i + 1 To iSize
            If iGrid(j) < iGrid(i) And iGrid(j) > 0 Then iCount = iCount + 1
        Next j
    Next i
    If iSide Mod 2 Then
        IsSolvable = Not iCount Mod 2
    Else
        IsSolvable = iCount Mod 2 = GetX(FindTile(0)) Mod 2
    End If
End Function

Function TestGrid() As Boolean
    Dim i As Integer

    For i = 1 To iSize - 1
        If Not iGrid(i) = i Then
            TestGrid = False
            Exit Function
        End If
    Next i
    TestGrid = True
End Function

Function FindTile(ByVal iNum As Integer) As Integer
    Dim i As Integer
    For i = 1 To iSize
        If iGrid(i) = iNum Then
            FindTile = i
            Exit Function
        End If
    Next i
End Function

Function ValidMove(ByVal i As Integer) As Boolean
    Dim e As Integer
    Dim xDiff As Integer
    Dim yDiff As Integer

    e = FindTile(0)
    xDiff = GetX(i) - GetX(e)
    yDiff = GetY(i) - GetY(e)
    If xDiff = 0 Then
        If yDiff = 1 Then
            sMessage = "Tile " & iGrid(i) & " was moved left"
            ValidMove = True
        ElseIf yDiff = -1 Then
            sMessage = "Tile " & iGrid(i) & " was moved right"
            ValidMove = True
        End If
    ElseIf yDiff = 0 Then
        If xDiff = 1 Then
            sMessage = "Tile " & iGrid(i) & " was moved up"
            ValidMove = True
        ElseIf xDiff = -1 Then
            sMessage = "Tile " & iGrid(i) & " was moved down"
            ValidMove = True
        End If
    End If
    If Not ValidMove Then sMessage = "Tile " & iGrid(i) & " may not be moved"
End Function

Function MoveTile(ByVal i As Integer)
    Dim e As Integer
    e = FindTile(0)
    iGrid(e) = iGrid(i)
    iGrid(i) = 0
    lMoves = lMoves + 1
End Function

Function ShowGrid()
    Dim x As Integer
    Dim y As Integer
    Dim i As Integer
    Dim sNum As String
    Dim s As String

    For x = 1 To iSide
        For y = 1 To iSide
            sNum = iGrid(GetI(x, y))
            If sNum = "0" Then sNum = ""
            s = s & sNum & vbTab
        Next y
        s = s & vbCr
    Next x
    If Not sMessage = "" Then
        s = s & vbCr & sMessage & vbCr
    End If
    ShowGrid = s
End Function