16 puzzle game

From Rosetta Code
Revision as of 05:42, 26 July 2018 by rosettacode>Gerard Schildberger (added the REXX computer programming language.)
16 puzzle game is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.


Task

Create 16 Puzzle Game.


See details: 16 Puzzle Game


Video: 16 Puzzle Game

REXX

<lang rexx>/*REXX pgm implements the 16 game; displays game grid, prompts for a move, game won? */ sep= copies('─',8); pad=left(,1+length(sep) ) /*pad=9 blanks. SEP is used for msgs.*/ parse arg N hard seed . /*obtain optional arguments from the CL*/ er= '***error***' /*literal used to indicate an error msg*/ if N== | N=="," then N= 4 /*Not specified? Then use the default.*/ if hard== | hard=="," then hard= 2 /* " " " " " " */ if \isInt(N) then do; say sep er "grid size isn't an integer: " N; exit 1; end if N<2 | N>9 then do; say sep er "grid size is out of range: " N; exit 1; end if isInt(seed) then call random , , seed /*use repeatability seed for RANDOM BIF*/ say sep 'Playing a ' N*N " game with a difficulity level of: " hard

  1. =0
        do   r=1  for N                         /* [◄]  build a solution for testing.  */
          do c=1  for N;   #= #+1;    @.r.c= #  /*bump number (count), define a cell.  */
          end   /*c*/
        end     /*r*/
                                                /* [↓]  HARD  is the puzzle difficulity*/
    do hard;     row= random(1)                 /*scramble the grid  HARD   # of times.*/
    if row  then call move random(1,N)substr('LR', random(1, 2), 1)   /* ◄── move row. */
            else call move substr('abcdefghi',random(1,N), 1)substr('+-',random(1,2),1)
    end   /*hard*/                                                    /* [↓]  move col.*/
                                                /*play 16─game until  solved  or  quit.*/
  do  until done()                              /*perform moves until puzzle is solved.*/
  call move                                     /*get user's move(s)  and  validate it.*/
  if errMsg\==  then do;  say sep er errMsg".";  iterate; end   /*possible error msg?*/
  end   /*until*/

call show; say sep 'Congratulations! The' N**2"─puzzle is solved." exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ done: #=0; do r=1 to N; do c=1 to N; #=#+1; if @.r.c\==# then return 0; end; end; return 1 isInt: return datatype( arg(1), 'W') /*return 1 if arg is a whole number. */ ghost: do r=1 for n; do c=1 for n;  !.r.c= @.r.c; end /*r*/; end /*c*/; return /*──────────────────────────────────────────────────────────────────────────────────────*/ move: arg x /*obtain optional move from ARG or ask.*/

     ask1= sep 'Please enter a     row  number    followed by a   L   or   R,       or'
     ask2= sep '       enter a   column letter    followed by a   +   or   -'   @quit
     @quit= '      (or Quit):'
     if x==  then do
                    if queued()==0  then do;   say;   call show;    say ask1;    say ask2
                                         end
                    pull x;  x= space(x, 0)     /*obtain a response;  elide whitespace.*/
                    end
     y= left(x, 1);          d= right(x, 1)     /*get a number or letter, and also a ± */
     num= isInt(d);        let= datatype(y,'U') /*get direction to shift, based on type*/
     if abbrev('QUIT', x, 1)  then do;  say;  say;  say sep  "quitting.";    exit;    end
              select
              when x ==                     then errMsg= "nothing entered"
              when length(x)>2                then errMsg= "improper response:  "       x
              when num  &  (y <1   | y >N  )  then errMsg= "row isn't in range: "       y
              when num  &  (d\="L" & d\='R')  then errMsg= "row shift isn't L or R: "   d
              when let  &  (y <"A" | y >HI )  then errMsg= "col isn't in range: "       y
              when let  &  (d\="+" & d\='-')  then errMsg= "col shift isn't + or -: "   d
              otherwise                            errMsg=
              end   /*select*/                  /* [↑]  verify the human entered data. */
     call ghost;    yn= pos(y, 'ABCDEFGHI')     /*create a ghost grid for easy moving. */
     if isInt(y)  then if d=='R'  then  do c=1  for N;  cm= c-1;  if c==1  then cm= c+N-1
                                                        @.y.c= !.y.cm
                                        end
                                  else  do c=1  for N;  cp= c+1;  if c==N  then cp= 1
                                                        @.y.c= !.y.cp
                                        end
                  else if d=='-'  then  do r=1  for N;  rm= r-1;  if r==1  then rm= r+N-1
                                                        @.r.yn= !.rm.yn
                                        end
                                  else  do r=1  for N;  rp= r+1;  if r==N  then rp= 1
                                                        @.r.yn= !.rp.yn
                                        end
     return

/*──────────────────────────────────────────────────────────────────────────────────────*/ show: top= '╔'copies( copies("═", 2)'╦', N); top= left( top, length(top) - 1)"╗"

     bar= '╠'copies( copies("═", 2)'╬', N);           bar= left( bar, length(bar) - 1)"╣"
     bot= '╚'copies( copies("═", 2)'╩', N);           bot= left( bot, length(bot) - 1)"╝"
     ind= left(,  3 + length(N) )                              /*compute indentation.*/
     col= ind  ind  ind' '   subword('a- b- c- d- e- f- g- h- i-', 1, n)
     HI= substr('abcdefghi', N, 1);    upper HI
     say col  ind  ind  ind  '-  means shift a column down';            say pad  ind  top
             do    r=1  for N;   z= r'R'    "  ║"                 /*build NxN game grid*/
                do c=1  for N;   z= z || right(@.r.c, 2)'║'       /*build  row by row. */
                end   /*c*/
             z= z   ' '   r"L"                                    /*add rightside info.*/
             if r==1  then z= z  pad'L  means shift a row left'   /* "   1st help info.*/
             if r==2  then z= z  pad'R  means shift a row right'  /* "   2nd   "    "  */
             say pad z;             if r\==N  then say pad  ind  bar
             end     /*r*/
     say pad  ind  bot;             say;
     say translate(col, '+', "-")   ind  ind  ind  '+  means shift a column up';  say
     return</lang>
output   when using the default inputs:
──────── Playing a  16  game with a difficulity level of:  2

                a- b- c- d-                -  means shift a column down
               ╔══╦══╦══╦══╗
          1R   ║ 1║ 2║15║ 4║   1L          L  means shift a row left
               ╠══╬══╬══╬══╣
          2R   ║ 5║ 6║ 3║ 8║   2L          R  means shift a row right
               ╠══╬══╬══╬══╣
          3R   ║12║ 9║ 7║11║   3L
               ╠══╬══╬══╬══╣
          4R   ║13║14║10║16║   4L
               ╚══╩══╩══╩══╝

                a+ b+ c+ d+                +  means shift a column up

──────── Please enter a     row  number    followed by a   L   or   R,       or
────────        enter a   column letter    followed by a   +   or   -       (or Quit):
c +                             ◄■■■■■■■■ user input                                                                                

                a- b- c- d-                -  means shift a column down
               ╔══╦══╦══╦══╗
          1R   ║ 1║ 2║ 3║ 4║   1L          L  means shift a row left
               ╠══╬══╬══╬══╣
          2R   ║ 5║ 6║ 7║ 8║   2L          R  means shift a row right
               ╠══╬══╬══╬══╣
          3R   ║12║ 9║10║11║   3L
               ╠══╬══╬══╬══╣
          4R   ║13║14║15║16║   4L
               ╚══╩══╩══╩══╝

                a+ b+ c+ d+                +  means shift a column up

──────── Please enter a     row  number    followed by a   L   or   R,       or
────────        enter a   column letter    followed by a   +   or   -       (or Quit):
3L                              ◄■■■■■■■■ user input
                a- b- c- d-                -  means shift a column down
               ╔══╦══╦══╦══╗
          1R   ║ 1║ 2║ 3║ 4║   1L          L  means shift a row left
               ╠══╬══╬══╬══╣
          2R   ║ 5║ 6║ 7║ 8║   2L          R  means shift a row right
               ╠══╬══╬══╬══╣
          3R   ║ 9║10║11║12║   3L
               ╠══╬══╬══╬══╣
          4R   ║13║14║15║16║   4L
               ╚══╩══╩══╩══╝

                a+ b+ c+ d+                +  means shift a column up

──────── Congratulations!   The 16─puzzle is solved.

Ring

<lang ring>

  1. Project : Sixteen Puzzle Game
  2. Date  : 2018/04/21
  3. Author : Gal Zsolt (~ CalmoSoft ~)
  4. Email  : <calmosoft@gmail.com>


load "guilib.ring" load "stdlib.ring"

app1 = new qapp {

       t1 = 0
       temp = ""
       table = [][]
       movesnr = 0
       button = list(16) 
       begintiles = list(16)
       pReturn = list(4)
       CounterMan = 0
       saveflag = 0
       stylefusionblack()  

       win1 = new qwidget() {
                  move(0,0)
                  resize(360, 600)
                  setwindowtitle("Calmosoft Sixteen Puzzle Game")
                  for n = 1 to 16
                        col = n%4
                        if col = 0 col = 4 ok
                        row = ceil(n/4)
                        button[n] = new qpushbutton(win1)
                        {
                                           setgeometry(60+col*40,60+row*40,40,40)                                            
                                           setstylesheet("color:white")    
                                           setstylesheet("background-color:blue")                                        
                                           settext(string(n))                                                                                        
                         } 
                  next
                 buttonup1 = new qpushbutton(win1)
                 {
                                     setgeometry(100, 60, 40, 40)
                                     settext("up")
                                     setclickevent("up1()")   
                 }
                 buttonup2 = new qpushbutton(win1)
                 {
                                     setgeometry(140, 60, 40, 40)
                                     settext("up")
                                     setclickevent("up2()")   
                 }
                buttonup3 = new qpushbutton(win1)
                 {
                                     setgeometry(180, 60, 40, 40)
                                     settext("up")
                                     setclickevent("up3()")   
                 }
                buttonup4 = new qpushbutton(win1)
                 {
                                     setgeometry(220, 60, 40, 40)
                                     settext("up")
                                     setclickevent("up4()")   
                 }
                 buttondown1 = new qpushbutton(win1)
                 {
                                         setgeometry(100, 260, 40, 40)
                                         settext("down")
                                         setclickevent("down1()")   
                 }
                 buttondown2 = new qpushbutton(win1)
                 {
                                         setgeometry(140, 260, 40, 40)
                                         settext("down")
                                         setclickevent("down2()")   
                 }
                 buttondown3 = new qpushbutton(win1)
                 {
                                         setgeometry(180, 260, 40, 40)
                                         settext("down")
                                         setclickevent("down3()")   
                 }
                buttondown4 = new qpushbutton(win1)
                 {
                                         setgeometry(220, 260, 40, 40)
                                         settext("down")
                                         setclickevent("down4()")   
                 }
                 buttonleft1 = new qpushbutton(win1)
                 {
                                         setgeometry(60, 100, 40, 40)
                                         settext("<<<")
                                         setclickevent("left1()")   
                 }
                 buttonleft2 = new qpushbutton(win1)
                 {
                                         setgeometry(60, 140, 40, 40)
                                         settext("<<<")
                                         setclickevent("left2()")   
                 }
                 buttonleft3 = new qpushbutton(win1)
                 {
                                         setgeometry(60, 180, 40, 40)
                                         settext("<<<")
                                         setclickevent("left3()")   
                 }
                 buttonleft4 = new qpushbutton(win1)
                 {
                                         setgeometry(60, 220, 40, 40)
                                         settext("<<<")
                                         setclickevent("left4()")   
                 }
                 buttonright1 = new qpushbutton(win1)
                 {
                                         setgeometry(260, 100, 40, 40)
                                         settext(">>>")
                                         setclickevent("right1()")   
                 }
                 buttonright2 = new qpushbutton(win1)
                 {
                                         setgeometry(260, 140, 40, 40)
                                         settext(">>>")
                                         setclickevent("right2()")   
                 }
                 buttonright3 = new qpushbutton(win1)
                 {
                                         setgeometry(260, 180, 40, 40)
                                         settext(">>>")
                                         setclickevent("right3()")   
                 }
                 buttonright4 = new qpushbutton(win1)
                 {
                                         setgeometry(260, 220, 40, 40)
                                         settext(">>>")
                                         setclickevent("right4()")   
                 }
                 buttonscramble = new qpushbutton(win1)
                 {
                                            setgeometry(100, 300, 160, 40)
                                            settext("Scarmble")
                                            setclickevent("scramble()")   
                 }
                 buttonreset = new qpushbutton(win1)
                 {
                                       setgeometry(100, 340, 160, 40)
                                       settext("Reset")
                                       setclickevent("reset()")   
                 }
                 buttonsave = new qpushbutton(win1)
                 {
                                  setgeometry(100, 380, 160, 40)
                                  settext("Save Game")
                                  setclickevent("psaveEmpty()")
                 }
                 buttonplay = new qpushbutton(win1)   
                 {
                                setgeometry(100,420,160,40)  
                                settext("Replay Game")  
                                setclickevent("pPlay()")
                 }
                 buttonnr = new qpushbutton(win1)
                 {
                                  setgeometry(100, 460, 160, 40)
                                  settext("Moves : ")
                 }
                 timebtn = new qpushbutton(win1)   
                 {
                                setgeometry(100,500,160,40)  
                                settext("Elapsed Time : ")  
                 }
                 t1 = clock()
                 for i = 1 to 16
                      begintiles[i] = string(i)
                 next
                 TimerMan = new qtimer(win1)
                 {
                                   setinterval(0.5)
                                   settimeoutevent("pTime()")
                                   stop()
                 }
                 show()
       }
       exec()

}

func scramble

      reset()
      empty = 16
      movesnr = 0
      buttonnr.settext("Moves : " + movesnr)
      for n= 1 to 1000  
           nr=random(15)+1
           up = (empty = (nr - 4))
           down = (empty = (nr + 4))
           left = ((empty = (nr - 1)) and ((nr % 4) != 1))
           right = ((empty = (nr + 1)) and ((nr % 4) != 0))
           move = up or down or left  or right
           if move = 1 
              temp1 = button[nr].text()
              temp2 = button[empty].text()
              button[empty].settext(temp1)
              button[nr].settext(temp2)
              empty = nr
           ok
      next
      timebtn.settext("Elapsed Time : ")
      t1 = clock()
      table = []
      saveflag = 0
      for n= 1 to 16
            if isstring(button[n].text())
               begintiles[n] = button[n].text()
            else
               begintiles[n] = string(button[n].text())
            ok 
      next

func reset

       movesnr = 0
       buttonnr.settext("Moves : " + movesnr)
       for i = 1 to 16
            button[i] {settext(string(i))}
            button[i].setstylesheet("background-color:blue")
            begintiles[i] = string(i)
       next
       timebtn.settext("Elapsed Time : ")
       t1 = clock()
       table = []
       saveflag = 0
       return

func pClock

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

func psaveEmpty

       timebtn.settext("Elapsed Time : ")
       t1 = clock()
       return

func psave

       if saveflag = 1
          for n = 1 to 4
               add(table, [pReturn[n], button[pReturn[n]].text()])
          next
       ok

func pPlay

       if saveflag = 1
          for n=1 to 16
                button[n]{settext(begintiles[n])}
                button[n].setstylesheet("background-color:blue")
          next
          timebtn.settext("Elapsed Time : ")
          movesnr = 0
          buttonnr.settext("Moves : " + movesnr)
          t1 = clock()
          CounterMan = 0
          TimerMan.start()
       ok

func pTime()

       if saveflag = 1
          CounterMan = CounterMan + 1
          if CounterMan > 1
             temp.setstylesheet("background-color:blue")
          ok
          pPlaySleep()
          sleep(1) 
          if CounterMan = len(table)
             TimerMan.stop()
          ok
       ok

func pPlaySleep

       pClock()
       button[table[CounterMan][1]].setstylesheet("background-color:orange") 
       temp =  button[table[CounterMan][1]]
       button[table[CounterMan][1]].settext(table[CounterMan][2])
       movesnr = movesnr + 1
       buttonnr.settext("Moves : " + movesnr)
       return

func up1

       temp = button[1].text()
       button[1].settext(button[5].text())
       button[5].settext(button[9].text())
       button[9].settext(button[13].text())
       button[13].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [1,5,9,13]
       saveflag = 1
       psave()
       return

func up2

       temp = button[2].text()
       button[2].settext(button[6].text())
       button[6].settext(button[10].text())
       button[10].settext(button[14].text())
       button[14].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [2,6,10,14]
       saveflag = 1
       psave()
       return

func up3

       temp = button[3].text()
       button[3].settext(button[7].text())
       button[7].settext(button[11].text())
       button[11].settext(button[15].text())
       button[15].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [3,7,11,15]
       saveflag = 1
       psave()
       return

func up4

       temp = button[4].text()
       button[4].settext(button[8].text())
       button[8].settext(button[12].text())
       button[12].settext(button[16].text())
       button[16].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [4,8,12,16]
       saveflag = 1
       psave()
       return

func down1

       temp = button[13].text()
       button[13].settext(button[9].text())
       button[9].settext(button[5].text())
       button[5].settext(button[1].text())
       button[1].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [13,9,5,1]
       saveflag = 1
       psave()
       return

func down2

       temp = button[14].text()
       button[14].settext(button[10].text())
       button[10].settext(button[6].text())
       button[6].settext(button[2].text())
       button[2].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [14,10,6,2]
       saveflag = 1
       psave()
       return

func down3

       temp = button[15].text()
       button[15].settext(button[11].text())
       button[11].settext(button[7].text())
       button[7].settext(button[3].text())
       button[3].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [15,11,7,3]
       saveflag = 1
       psave()
       return

func down4

       temp = button[16].text()
       button[16].settext(button[12].text())
       button[12].settext(button[8].text())
       button[8].settext(button[4].text())
       button[4].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [16,12,8,4]
       saveflag = 1
       psave()
       return

func left1

       temp = button[1].text()
       button[1].settext(button[2].text())
       button[2].settext(button[3].text())
       button[3].settext(button[4].text())
       button[4].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [1,2,3,4]
       saveflag = 1
       psave()
       return

func left2

       temp = button[5].text()
       button[5].settext(button[6].text())
       button[6].settext(button[7].text())
       button[7].settext(button[8].text())
       button[8].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [5,6,7,8]
       saveflag = 1
       psave()
       return

func left3

       temp = button[9].text()
       button[9].settext(button[10].text())
       button[10].settext(button[11].text())
       button[11].settext(button[12].text())
       button[12].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [9,10,11,12]
       saveflag = 1
       psave()
       return

func left4

       temp = button[13].text()
       button[13].settext(button[14].text())
       button[14].settext(button[15].text())
       button[15].settext(button[16].text())
       button[16].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [13,14,15,16]
       saveflag = 1
       psave()
       return

func right1

       temp = button[4].text()
       button[4].settext(button[3].text())
       button[3].settext(button[2].text())
       button[2].settext(button[1].text())
       button[1].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [4,3,2,1]
       saveflag = 1
       psave()
       return

func right2

       temp = button[8].text()
       button[8].settext(button[7].text())
       button[7].settext(button[6].text())
       button[6].settext(button[5].text())
       button[5].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [8,7,6,5]
       saveflag = 1
       psave()
       return

func right3

       temp = button[12].text()
       button[12].settext(button[11].text())
       button[11].settext(button[10].text())
       button[10].settext(button[9].text())
       button[9].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [12,11,10,9]
       saveflag = 1
       psave()
       return

func right4

       temp = button[16].text()
       button[16].settext(button[15].text())
       button[15].settext(button[14].text())
       button[14].settext(button[13].text())
       button[13].settext(temp)
       movesnr =movesnr + 1
       buttonnr.settext("Moves : " + string(movesnr))
       pClock()
       pReturn = [16,15,14,13]
       saveflag = 1
       psave()
       return

</lang> Output image:

16 Puzzle Game