draughts_engine.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #!/usr/bin/python3
  2. import re
  3. def draw_checkerboard(status,space="▒",white_norm="h",white_knight="H",black_norm="b",black_knight="B",empty=" ",column="12345678",frstrow=" abcdefgh\n"):
  4. """ Draw a checkerboard, status is the virtual representation of the board, as a bi-dimensional array, white/black norm/knight are the representation of the pieces
  5. space is a non-walkable cell, empty is a walkable cell, column contains labels for the left column, frstrow the labels for the first row.
  6. """
  7. bstr = ""
  8. bstr += frstrow
  9. for row in range(0,len(status)):
  10. bstr += column[row]
  11. if row % 2 == 0:
  12. bstr += space
  13. for cell in range(0,int(len(status)/2)):
  14. if status[row][cell] == 1: #black normal piece
  15. bstr += black_norm
  16. elif status[row][cell] == 2: #white normal piece
  17. bstr += white_norm
  18. elif status[row][cell] == 3: #black knight piece
  19. bstr += black_knight
  20. elif status[row][cell] == 4: #white knight piece
  21. bstr += white_knight
  22. else: #empty
  23. bstr += empty
  24. if (cell < 3):
  25. bstr += space
  26. if row % 2 !=0: #odd lines ends with space and a line feed, even ones just with the line feed
  27. bstr += space+"\n"
  28. else:
  29. bstr += "\n"
  30. return bstr
  31. def position_resolver(pos,board):
  32. """Checks if the position is valid and wether it contains a piece or not"""
  33. row,col = row_column_translation(pos[1],pos[0])
  34. if (col == -1):
  35. return -1
  36. else:
  37. return board[row][col]
  38. def row_column_translation(row,col):
  39. """Converts a coordinate made by a letter/number couple in the index usable with the array that represents the board"""
  40. row = int(row) - 1
  41. if row % 2 != 0:
  42. cols = range(ord("a"),ord("a")+7,2)
  43. else:
  44. cols = range(ord("b"),ord("b")+7,2)
  45. if ord(col) not in cols:
  46. return -1,-1
  47. else:
  48. return row,cols.index(ord(col))
  49. def traslate_coord(pos):
  50. for en,p in enumerate(pos):
  51. temp_col = chr( (int(pos[en][1])-1)+ord("a") )
  52. temp_row = ord(pos[en][0])-ord("a")
  53. pos[en] = temp_col
  54. pos[en] += str(temp_row +1)
  55. return pos
  56. def valid_move(pos_toParse,turn,board,inversion=False):
  57. """Checks if the move is valid, execute it and returns the updated board"""
  58. #pos = pos_toParse.split(" ") #tricky part, pos[0] is the starting cell, pos[1] to pos[n] the destination point(s), for every coord, [x][0] is the column (the letter) and [x][1] the row (the number)
  59. pos = re.findall("\\b[abcdefgh][12345678]\\b",pos_toParse) #both row-col and col-row are accepted
  60. if len(pos) == 0:
  61. pos = re.findall("\\b[12345678][abcdefgh]\\b",pos_toParse)
  62. for en,x in enumerate(pos):
  63. pos[en] = x[1]+x[0]
  64. if inversion:
  65. pos = traslate_coord(pos)
  66. if len(pos) < 2: #less than 2 coords were given, movement is invalid
  67. return -1
  68. row_start,col_start = row_column_translation(pos[0][1],pos[0][0])
  69. if(row_start == -1):
  70. return -1
  71. if turn == 1: #black turn
  72. valid_piece = (1,3)
  73. foe_piece = (2,4)
  74. else: #white turn
  75. valid_piece = (2,4)
  76. foe_piece = (1,3)
  77. start_cell = position_resolver(pos[0],board)
  78. if start_cell not in valid_piece: #selected an opponent's piece or an empty cell
  79. return -1
  80. if ((start_cell == 1 and abs(ord(pos[1][0]) - ord(pos[0][0])) == 1 and ord(pos[0][1]) - ord(pos[1][1]) == -1) or (start_cell == 2 and abs(ord(pos[1][0]) - ord(pos[0][0])) == 1 and ord(pos[0][1]) - ord(pos[1][1]) == 1) or ((start_cell == 4 or start_cell == 3) and abs(ord(pos[1][0]) - ord(pos[0][0])) == 1 and abs(ord(pos[1][1]) - ord(pos[0][1])) == 1) and len(pos) == 2 ):
  81. move_to = position_resolver(pos[1],board) #check non-capturing movement, to be valid it must be 1 col shift and 1 row shift, for norm pieces direction is important, and it must be a single move
  82. if (move_to == 0): #if the cell is empty, the move is valid
  83. row,col = row_column_translation(pos[0][1],pos[0][0])
  84. board[row][col] = 0
  85. row,col = row_column_translation(pos[1][1],pos[1][0])
  86. if ((row == 0) and start_cell == 2): #piece promotion
  87. start_cell = 4
  88. if ((row == 7) and start_cell == 1):
  89. start_cell = 3
  90. board[row][col] = start_cell
  91. return board
  92. else: #destination cell is not empty
  93. return -1
  94. else:
  95. for x in range(0,len(pos) -1):
  96. if (start_cell == 1 and abs(ord(pos[1][0]) - ord(pos[0][0])) == 2 and ord(pos[0][1]) - ord(pos[1][1]) == -2) or (start_cell == 2 and abs(ord(pos[1][0]) - ord(pos[0][0])) == 2 and ord(pos[0][1]) - ord(pos[1][1]) == 2) or ((start_cell == 4 or start_cell == 3) and abs(ord(pos[1][0]) - ord(pos[0][0])) == 2 and abs(ord(pos[1][1]) - ord(pos[0][1])) == 2):
  97. move_to = position_resolver(pos[1],board)
  98. if (move_to == 0): #check if destination is empty
  99. shift_x = int((ord(pos[1][0]) - ord(pos[0][0]))/2)
  100. shift_y = int((ord(pos[1][1]) - ord(pos[0][1]))/2)
  101. if (start_cell == 1):#normal pieces can capture kings (to do: optional rule)
  102. foe_piece = (2,4)
  103. #foe_piece = (2,)
  104. elif (start_cell == 2):
  105. foe_piece = (1,3)
  106. #foe_piece = (1,)
  107. row,col = row_column_translation(chr(ord(pos[0][1])+shift_y),chr(ord(pos[0][0])+shift_x)) #check if an opponent piece lies in between start and destination
  108. if (board[row][col] in foe_piece):
  109. board[row][col] = 0
  110. row,col = row_column_translation(pos[0][1],pos[0][0])
  111. board[row][col] = 0
  112. row,col = row_column_translation(pos[1][1],pos[1][0])
  113. board[row][col] = start_cell
  114. if ((row == 0) and start_cell == 2): #piece promotion
  115. start_cell = 4
  116. if ((row == 7) and start_cell == 1):
  117. start_cell = 3
  118. pos.pop(0) #if there are other moves this check loops
  119. else: #one of the pieces moves are invalid
  120. return -1
  121. else: #destination not empty
  122. return -1
  123. else:
  124. return -1
  125. return board
  126. def init_board():
  127. checkerboard = []
  128. for row in range(0,8):
  129. if (row <= 2):
  130. checkerboard.append([1,1,1,1])
  131. elif (row >= 5):
  132. checkerboard.append([2,2,2,2])
  133. else:
  134. checkerboard.append([0,0,0,0])
  135. return checkerboard
  136. def checkWin(board):
  137. black_won = True
  138. white_won = True
  139. for row in board:
  140. if (1 in row) or (3 in row):
  141. white_won = False
  142. if (2 in row) or (4 in row):
  143. black_won = False
  144. return (white_won,black_won)
  145. def main():
  146. import sys
  147. column_i = "12345678"
  148. frstrow_i = " abcdefgh\n"
  149. inverted = False
  150. try:
  151. if sys.argv[1] == "i":
  152. column_i = "abcdefgh"
  153. frstrow_i = " 12345678\n"
  154. inverted = True
  155. except IndexError:
  156. pass
  157. main_board = init_board()
  158. s = ""
  159. turn = False
  160. while (s !="q"):
  161. vis_board = draw_checkerboard(main_board,column=column_i,frstrow=frstrow_i)
  162. print(vis_board)
  163. if turn:
  164. s = input("Black move:")
  165. else:
  166. s = input("White move:")
  167. if (s !="q"):
  168. result = valid_move(s,turn,main_board,inversion=inverted)
  169. if result != -1:
  170. main_board = result
  171. winner = checkWin(main_board)
  172. if winner[0]:
  173. print("White won this game.")
  174. return turn
  175. if winner[1]:
  176. print("Black won this game.")
  177. return turn
  178. turn = not turn
  179. else:
  180. print("Invalid move")
  181. return -1
  182. if __name__ == "__main__":
  183. main()
  184. quit()