# ==============================================================================
"""SLIDE : a color-based slide puzzle game"""
# ==============================================================================
__author__  = "Christophe Schlick modified by Philippe Blasi"
__version__ = "0.0" # skeleton version
__date__    = "2022-11-12"
# ==============================================================================
from ezTK import *
from random import shuffle, randrange as rr
# ------------------------------------------------------------------------------
def main():
  """create the main window and pack the widgets"""
  global win
  win = Win(title='SLIDE', op=5, click=on_click) # create main window
  colors = ('#FFF','#F00','#0F0','#07F','#FF0') # colors for board cells
  # ----------------------------------------------------------------------------
  # TODO (use 1 Frame with 2 Buttons, 1 Frame with 25 Bricks)
  # ----------------------------------------------------------------------------
  on_shuffle(); win.loop() # set random configuration and start event loop
# ------------------------------------------------------------------------------
def on_shuffle() -> None:
  """callback function for the "SHUFFLE" button"""
  # TODO (use 'win.cells' a 1D list of integers to store initial cell states)
  # TODO (create random board configuration by shuffling the previous list)
# ------------------------------------------------------------------------------
def on_reset() -> None:
  """callback function for the "RESET" button"""
  # TODO (reset board configuration to its initial state)
  # TODO (use 'win.grid' a 2D list of integers to store current board config)
# ------------------------------------------------------------------------------
def on_click(widget:object, code:str, mods:str) -> None:
  """callback function for all keyboard events"""
  if widget.master != win.board: return # wrong click (= not on a board cell)
  row, col = widget.index; move(row, col) # apply move on selected cell
# ------------------------------------------------------------------------------
def move(row:int, col:int) -> None:
  """move cell located at (row,col) to its neighboring empty cell (if any)"""
  # TODO (look at the 4 neighboring cells to check if one is the empty cell)
  # TODO (then move the selected cell to the location of the empty cell)
  if check(): win.grid = []; victory() # start animation if victory
  else: show() # show new board configuration after cell displacement
# ------------------------------------------------------------------------------
def show() -> None:
  """show current game board by setting state defined for each grid cell"""
  for n in range(25): # loop over grid cells and change state of board cells
    row, col = n//5, n%5; win.board[row][col].state = win.grid[row][col]
# ------------------------------------------------------------------------------
def check() -> bool:
  """check victory by counting number of peer cells with same state"""
  for loop in range(25): # loop over all board cells
    if loop == 12: continue # skip central cell (should be empty, anyway)
    row, col = loop//5, loop%5 # get cell coords by Euclidian division
    peers = [win.grid[row+r][col+c] for r,c in ((1,0),(-1,0),(0,1),(0,-1))
      if 0 <= row+r <= 4 and 0 <= col+c <= 4] # get states for all peer cells
    if peers.count(win.grid[row][col]) not in (2,3): return False # no victory
  return True # all cells have 2 or 3 peers with same state ==> victory
# ------------------------------------------------------------------------------
def victory() -> None:
  """play victory animation"""
  win.board[rr(5)][rr(5)].state = rr(1,5) # change color of random grid cell
  if not win.grid: win.after(10, victory) # launch 'victory' every 10 ms
# ==============================================================================
if __name__ == '__main__':
  main()
# ==============================================================================
