Advertisement
here2share

# Tk_15_Puzzle_Game.py

Feb 3rd, 2022
1,076
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.74 KB | None | 0 0
  1. # Tk_15_Puzzle_Game.py
  2.  
  3. from tkinter import *
  4. from tkinter import messagebox
  5. import random
  6.  
  7. class Board:
  8.     def __init__(self, playable=True):
  9.         while True:
  10.             # list of text for game squares:
  11.             self.lot = [str(i) for i in range(1,16)] + ['']
  12.             if not playable:
  13.                 break
  14.             # list of text for game squares randomized:
  15.             random.shuffle(self.lot)
  16.             if self.is_solvable():
  17.                 break
  18.  
  19.         # game board is 2D array of game squares:
  20.         self.bd = []
  21.         i = 0
  22.         for r in range(4):
  23.             row = []
  24.             for c in range(4):
  25.                 row.append(Square(r,c,self.lot[i]))
  26.                 i += 1
  27.             self.bd.append(row)
  28.  
  29.     def is_solvable(self):
  30.         inv = self.get_inversions()
  31.         odd = self.is_odd_row()
  32.         if inv % 2 == 0 and odd:
  33.             return True
  34.         if inv % 2 == 1 and not odd:
  35.             return True
  36.         return False
  37.  
  38.     def get_inversions(self):
  39.         cnt = 0
  40.         for i, x in enumerate(self.lot[:-1]):
  41.             if x != '':
  42.                 for y in self.lot[i+1:]:
  43.                     if y != '' and int(x) > int(y):
  44.                         cnt += 1
  45.         return cnt
  46.  
  47.     # returns True if open square is in odd row from bottom:
  48.     def is_odd_row(self):
  49.         idx = self.lot.index('')
  50.         return idx in [4,5,6,7,12,13,14,15]          
  51.  
  52.     # returns name, text, and button object at row & col:
  53.     def get_item(self, r, c):
  54.         return self.bd[r][c].get()
  55.  
  56.     def get_square(self, r, c):
  57.         return self.bd[r][c]
  58.  
  59.     def game_won(self):
  60.         goal = [str(i) for i in range(1,16)] + ['']
  61.         i = 0
  62.         for r in range(4):
  63.             for c in range(4):
  64.                 nm, txt, btn = self.get_item(r,c)
  65.                 if txt != goal[i]:
  66.                     return False
  67.                 i += 1
  68.         return True
  69.  
  70. # ************************************************
  71.  
  72. class Square:       # ['btn00', '0', None]
  73.     def __init__(self, row, col, txt):
  74.         self.row = row
  75.         self.col = col
  76.         self.name = 'btn' + str(row) + str(col)
  77.         self.txt = txt
  78.         self.btn = None
  79.  
  80.     def get(self):
  81.             return [self.name, self.txt, self.btn]
  82.  
  83.     def set_btn(self, btn):
  84.         self.btn = btn
  85.  
  86.     def set_txt(self, txt):
  87.         self.txt = txt
  88.  
  89. # ************************************************
  90.  
  91. class Game:
  92.     def __init__(self, gw):
  93.         self.window = gw
  94.  
  95.         # game data:
  96.         self.bd = None
  97.         self.playable = False
  98.  
  99.         # top frame:
  100.         self.top_fr = Frame(gw,
  101.                             width=520,
  102.                             height=120,
  103.                             bg='light green')
  104.         self.top_fr.pack(fill=X)
  105.  
  106.         self.hdg = Label(self.top_fr,
  107.                          text=' 15 PUZZLE GAME ',
  108.                          font='arial 32 bold',
  109.                          fg='Navy Blue',
  110.                          bg='white')
  111.         self.hdg.place(relx=0.5, rely=0.35,
  112.                        anchor=CENTER)
  113.  
  114.         self.dir = Label(self.top_fr,
  115.                  text="CLICK 'NEW GAME' TO BEGIN",
  116.                  font='arial 12 ',
  117.                  fg='Navy Blue',
  118.                  bg='light green')
  119.         self.dir.place(x=20, rely=0.8,
  120.                        anchor=W)
  121.  
  122.         self.play_btn = Button(self.top_fr,
  123.                                text='NEW GAME',
  124.                                bd=5,
  125.                                bg='PaleGreen4',
  126.                                fg='White',
  127.                                font='arial 12 bold',
  128.                                command=self.new_game)
  129.         self.play_btn.place(relx=0.95, rely=0.8,
  130.                        anchor=E)
  131.  
  132.         # bottom frame:
  133.         self.btm_fr = Frame(gw,
  134.                             width=520,
  135.                             height=500,
  136.                             bg='light steel blue')
  137.         self.btm_fr.pack(fill=X)
  138.  
  139.         # board frame:
  140.         self.bd_fr = Frame(self.btm_fr,
  141.                            width=400+2,
  142.                            height=400+2,
  143.                            relief='solid',
  144.                            bd=1,
  145.                            bg='lemon chiffon')
  146.         self.bd_fr.place(relx=0.5, rely=0.5,
  147.                          anchor=CENTER)
  148.  
  149.         self.play_game()
  150.  
  151. # ************************************************
  152.  
  153.     def new_game(self):
  154.         self.playable = True
  155.         self.dir.config(text='CLICK ON A TILE TO MOVE')
  156.         self.play_game()
  157.  
  158.     def play_game(self):
  159.         # place squares on board:
  160.         if self.playable:
  161.             btn_state = 'normal'
  162.         else:
  163.             btn_state = 'disable'
  164.         self.bd = Board(self.playable)               
  165.         objh = 100  # widget height
  166.         objw = 100  # widget width
  167.         objx = 0    # x-position of widget in frame
  168.         objy = 0    # y-position of widget in frame
  169.  
  170.         for r in range(4):
  171.             for c in range(4):
  172.                 nm, txt, btn = self.bd.get_item(r,c)
  173.                 bg_color = 'RosyBrown1'
  174.                 if txt == '':
  175.                     bg_color = 'White'           
  176.                 game_btn = Button(self.bd_fr,
  177.                                   text=txt,
  178.                                   relief='solid',
  179.                                   bd=1,
  180.                                   bg=bg_color,
  181.                                   fg='gray',
  182.                                   font='arial 25 bold',
  183.                                   state=btn_state,
  184.                                   command=lambda x=nm: self.clicked(x))
  185.                 game_btn.place(x=objx, y=objy,
  186.                                height=objh, width=objw)
  187.  
  188.                 sq = self.bd.get_square(r,c)
  189.                 sq.set_btn(game_btn)
  190.  
  191.                 objx = objx + objw
  192.             objx = 0
  193.             objy = objy + objh
  194.  
  195.     # processing when a square is clicked:
  196.     def clicked(self, nm):
  197.         self.dir.config(text='')
  198.         r, c = int(nm[3]), int(nm[4])
  199.         nm_fr, txt_fr, btn_fr = self.bd.get_item(r,c)
  200.  
  201.         # cannot 'move' open square to itself:
  202.         if not txt_fr:
  203.             self.dir.config(text='SELECT "TILE" TO MOVE')
  204.             return
  205.  
  206.         # 'move' square to open square if 'adjacent' to it:            
  207.         adjs = [(r-1,c), (r, c-1), (r, c+1), (r+1, c)]
  208.         for x, y in adjs:
  209.             if 0 <= x <= 3 and 0 <= y <= 3:
  210.                 nm_to, txt_to, btn_to = self.bd.get_item(x,y)
  211.                 if not txt_to:
  212.                     sq = self.bd.get_square(x,y)
  213.                     sq.set_txt(txt_fr)
  214.                     sq = self.bd.get_square(r,c)
  215.                     sq.set_txt(txt_to)
  216.                     btn_to.config(text=txt_fr,
  217.                                   bg='RosyBrown1')
  218.                     btn_fr.config(text=txt_to,
  219.                                   bg='White')
  220.                     # check if game is won:              
  221.                     if self.bd.game_won():
  222.                         self.dir.config(text='YOU WON !!!')
  223.                         ans = messagebox.askquestion(
  224.                             'Congrats !!!', 'PLAY AGAIN?')
  225.                         if ans == 'no':
  226.                             self.window.destroy()
  227.                         else:
  228.                             self.new_game()
  229.                     return
  230.  
  231.         # cannot move 'non-adjacent' square to open square:
  232.         self.dir.config(text='ILLIGAL MOVE, TRY AGAIN')
  233.         return
  234.  
  235. # ************************************************
  236.  
  237. root = Tk()
  238. root.title('15 Puzzle Game')
  239. root.geometry('440x560+100+50')
  240. root.resizable(False, False)
  241. g = Game(root)
  242. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement