You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
497 lines
22 KiB
497 lines
22 KiB
from copy import deepcopy
|
|
from typing import List, Tuple
|
|
from collections import Counter
|
|
|
|
# 棋子配置
|
|
BOARD = [["--", "--", "redElephant", "--", "--", "--", "blackMouse", "--", "--"],
|
|
["--", "redBird", "--", "--", "--", "--", "--", "blackFox", "--"],
|
|
["--", "--", "redWolf", "--", "--", "--", "blackCheetah", "--", "--"],
|
|
["--", "--", "redLion", "--", "--", "--", "blackLion", "--", "--"],
|
|
["--", "--", "redCheetah", "--", "--", "--", "blackWolf", "--", "--"],
|
|
["--", "redFox", "--", "--", "--", "--", "--", "blackBird", "--"],
|
|
["--", "--", "redMouse", "--", "--", "--", "blackElephant", "--", "--"]]
|
|
DIMENSION_ROW = 7
|
|
DIMENSION_COL = 9
|
|
|
|
# 河流配置
|
|
WATER_BLOCK = [(1,3), (1,4), (1,5),
|
|
(2,3), (2,4), (2,5),
|
|
(4,3), (4,4), (4,5),
|
|
(5,3), (5,4), (5,5)]
|
|
WATER_WIDTH = 3
|
|
WATER_HEIGHT = 2
|
|
|
|
# 陷阱配置
|
|
RED_TRAP_BLOCK = [(2, 0), (3, 1), (4, 0)]
|
|
BLACK_TRAP_BLOCK = [(2, 8), (3, 7), (4, 8)]
|
|
|
|
# 巢穴配置
|
|
RED_DEN_BLOCK = (3, 0)
|
|
BLACK_DEN_BLOCK = (3, 8)
|
|
DEN_BLOCK = [RED_DEN_BLOCK, BLACK_DEN_BLOCK]
|
|
|
|
# 动物种数
|
|
ANIMAL_SPECIES = 7
|
|
|
|
# 动物名字
|
|
ANIMAL_NAME_SHORT = "MBFWCLE"
|
|
ANIMAL_NAME = { ANIMAL_NAME_SHORT[i]: [
|
|
"Mouse",
|
|
"Bird",
|
|
"Fox",
|
|
"Wolf",
|
|
"Cheetah",
|
|
"Lion",
|
|
"Elephant"
|
|
][i] for i in range(ANIMAL_SPECIES)}
|
|
|
|
# 动物强度
|
|
ANIMAL_POWER = { ANIMAL_NAME_SHORT[i] : i + 1 for i in range(ANIMAL_SPECIES) }
|
|
|
|
|
|
class GameState:
|
|
def __init__(self):
|
|
|
|
self.board = [ [ (BOARD[i][j][0], BOARD[i][j][3] if BOARD[i][j][0] == 'r' else \
|
|
BOARD[i][j][0] if BOARD[i][j][0] == '-' else \
|
|
BOARD[i][j][5] ) \
|
|
for j in range(DIMENSION_COL)] for i in range(DIMENSION_ROW)]
|
|
|
|
self.waterBlock = WATER_BLOCK
|
|
self.denBlock = DEN_BLOCK
|
|
|
|
self.moveFunctions = {"M": self.mouseMove,
|
|
"B": self.birdMove,
|
|
"F": self.foxMove,
|
|
"W": self.standardMove,
|
|
"C": self.standardMove,
|
|
"L": self.lionMove,
|
|
"E": self.standardMove}
|
|
|
|
self.red_to_move = True
|
|
self.history = []
|
|
|
|
self.conquered = False
|
|
self.draw = False
|
|
|
|
self.queue7 = []
|
|
self.queue17 = []
|
|
|
|
def queueMoveToken(self, targetRow:int, targetCol:int, color:str, animal:str) -> str:
|
|
return f"{color}{animal}{targetRow}{targetCol}"
|
|
|
|
def isFault7(self, move) -> bool:
|
|
if len(self.queue7) < 6:
|
|
return False
|
|
else:
|
|
status = Counter(self.queue7 + [f"{move.nowRow}{move.nowCol}{move.nowStat}"] )\
|
|
.most_common()[0][1] >= 3
|
|
self.queue7.pop(0)
|
|
return status
|
|
|
|
def isFault17(self, move):
|
|
if len(self.queue17) < 16:
|
|
return False
|
|
else:
|
|
counter = Counter(self.queue17 + [f"{move.nowRow}{move.nowCol}{move.nowStat}"])
|
|
status = (len(counter) <= 5)
|
|
self.queue17.pop(0)
|
|
return status
|
|
|
|
def makeMove(self, move) -> bool:
|
|
''' 下棋,返回值表示是否吃子 '''
|
|
self.board[move.nowRow][move.nowCol] = ("-", "-")
|
|
self.board[move.targetRow][move.targetCol] = move.nowStat
|
|
# 记录,悔棋用
|
|
self.history.append(move)
|
|
# 更新 7-3 违例 和 17-5 违例队列
|
|
self.queue7.append(self.queueMoveToken(move.targetRow, move.targetCol,\
|
|
move.nowStat[0], move.nowStat[1]))
|
|
self.queue17.append(self.queueMoveToken(move.targetRow, move.targetCol,\
|
|
move.nowStat[0], move.nowStat[1]))
|
|
# 回合交换
|
|
self.red_to_move = not self.red_to_move
|
|
return move.isTargetEaten
|
|
|
|
def undoMove(self):
|
|
''' 悔棋 '''
|
|
if len(self.history) != 0: # make sure that there is a move to undo
|
|
move = self.history.pop()
|
|
self.board[move.nowRow][move.nowCol] = move.nowStat
|
|
self.board[move.targetRow][move.targetCol] = move.targetStat
|
|
self.red_to_move = not self.red_to_move # swap players
|
|
self.conquered = False
|
|
self.draw = False
|
|
|
|
def getValidMoves(self) -> Tuple[List, bool]:
|
|
''' 获取所有可能的走法 '''
|
|
moves = []
|
|
|
|
for row in range(len(self.board)):
|
|
for col in range(len(self.board[row])):
|
|
turn = self.board[row][col][0]
|
|
if (turn == "r" and self.red_to_move)\
|
|
or (turn == "b" and not self.red_to_move):
|
|
self.moveFunctions[self.board[row][col][1]](row, col, moves)
|
|
# 死棋,判负
|
|
if len(moves) == 0:
|
|
print("No moves?")
|
|
return moves, True
|
|
else:
|
|
self.conquered = False
|
|
self.draw = False
|
|
return moves, False
|
|
|
|
def inWater(self,row,col):
|
|
''' 判断是否在水中 '''
|
|
return True if (row, col) in WATER_BLOCK else False
|
|
|
|
def jumpConditions(self, nowRow, nowCol, targetRow, targetCol, jumpDirectionRow, jumpDirectionCol, enemyColor):
|
|
''' 判断狮子是否满足跳河条件 '''
|
|
# 如果狮子可以纵向向上跳河(河对面为空地或敌方棋子)
|
|
if jumpDirectionRow < 0 and\
|
|
self.board[targetRow + (WATER_HEIGHT * jumpDirectionRow)][targetCol][0] in ['-', enemyColor]:
|
|
# 如果路径中有老鼠,则不满足
|
|
for i in range(WATER_HEIGHT):
|
|
if self.board[targetRow + i * jumpDirectionRow][targetCol][1] == "M":
|
|
return False
|
|
|
|
# 如果河对面没有任何动物,则狮子可以跳河
|
|
if self.board[targetRow + (WATER_HEIGHT * jumpDirectionRow)][targetCol][0] == "-":
|
|
return True
|
|
# 如果河对面为敌方动物,且比狮子弱,则狮子可以跳河
|
|
elif self.isEatable(nowRow, nowCol, targetRow + (WATER_HEIGHT * jumpDirectionRow), targetCol):
|
|
return True
|
|
|
|
# 如果狮子选择纵向向下跳河(河对面为空地或敌方棋子)
|
|
elif jumpDirectionRow > 0 and\
|
|
self.board[targetRow + (WATER_HEIGHT * jumpDirectionRow)][targetCol][0] in ['-', enemyColor]:
|
|
# 如果路径中有老鼠,则不满足
|
|
for i in range(WATER_HEIGHT):
|
|
if self.board[targetRow + i * jumpDirectionRow][targetCol][1] == "M":
|
|
return False
|
|
# 如果河对面没有任何动物,则狮子可以跳河
|
|
if self.board[targetRow + (WATER_HEIGHT * jumpDirectionRow)][targetCol][0] == "-":
|
|
return True
|
|
# 如果河对面为敌方动物,且比狮子弱,则狮子可以跳河
|
|
elif self.isEatable(nowRow, nowCol, targetRow + (WATER_HEIGHT * jumpDirectionRow), targetCol):
|
|
return True
|
|
|
|
# 如果狮子选择横向跳河
|
|
elif jumpDirectionCol != 0 and\
|
|
self.board[targetRow][targetCol + (WATER_WIDTH * jumpDirectionCol)][0] in ['-', enemyColor]:
|
|
# 如果路径中有老鼠,则不满足
|
|
for i in range(WATER_WIDTH):
|
|
if self.board[targetRow + i * jumpDirectionCol][targetCol][1] == "M":
|
|
return False
|
|
# 如果河对面没有任何动物,则狮子可以跳河
|
|
if self.board[targetRow][targetCol + (WATER_WIDTH - 1) * jumpDirectionCol][0] == "-":
|
|
return True
|
|
# 如果河对面为敌方动物,且比狮子弱,则狮子可以跳河
|
|
elif self.isEatable(nowRow, nowCol, targetRow , targetCol + (WATER_WIDTH * jumpDirectionCol)):
|
|
return True
|
|
return False
|
|
|
|
def isFriendBase(self, targetRow, targetCol, enemyColor):
|
|
''' 判断是否为我方基地 '''
|
|
if enemyColor == "b" and\
|
|
targetRow == RED_DEN_BLOCK[0] and\
|
|
targetCol == RED_DEN_BLOCK[1]:
|
|
return True
|
|
elif enemyColor == "r" and\
|
|
targetRow == BLACK_DEN_BLOCK[0] and\
|
|
targetCol == BLACK_DEN_BLOCK[1]:
|
|
return True
|
|
return False
|
|
|
|
def isEnemyBase(self, targetRow, targetCol, enemyColor):
|
|
''' 判断是否为敌方基地 '''
|
|
if enemyColor == "b" and\
|
|
targetRow == BLACK_DEN_BLOCK[0] and\
|
|
targetCol == BLACK_DEN_BLOCK[1]:
|
|
return True
|
|
elif enemyColor == 'r' and\
|
|
targetRow == RED_DEN_BLOCK[0] and\
|
|
targetCol == RED_DEN_BLOCK[1]:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def enemyConquerDen(self):
|
|
''' 判断是否到达敌方基地 '''
|
|
if self.board[RED_DEN_BLOCK[0]] [RED_DEN_BLOCK[1]] [0] == "b" \
|
|
or self.board[BLACK_DEN_BLOCK[0]][BLACK_DEN_BLOCK[1]][0] == "r":
|
|
self.conquered = True
|
|
return True
|
|
return False
|
|
|
|
def isFriendTrap(self, targetRow, targetCol, enemyColor):
|
|
''' 判断是否为我方陷阱'''
|
|
if (enemyColor == 'b') and ((targetRow, targetCol) in RED_TRAP_BLOCK):
|
|
return True
|
|
elif (enemyColor == 'r') and ((targetRow, targetCol) in BLACK_TRAP_BLOCK):
|
|
return True
|
|
return False
|
|
|
|
def isEnemyTrap(self, targetRow, targetCol, enemyColor):
|
|
''' 判断是否为敌方陷阱 '''
|
|
if (enemyColor == 'b') and ((targetRow, targetCol) in BLACK_TRAP_BLOCK):
|
|
return True
|
|
elif (enemyColor == 'r') and ((targetRow, targetCol) in RED_TRAP_BLOCK):
|
|
return True
|
|
return False
|
|
|
|
def isEatable(self, row, col, targetRow, targetCol):
|
|
''' 判断能否吃掉敌方动物 '''
|
|
friend = self.board[row][col]
|
|
enemy = self.board[targetRow][targetCol]
|
|
# 如果目标不是动物,直接返回
|
|
if enemy[0] == "-":
|
|
return False
|
|
# 陷阱逻辑
|
|
# 自家陷阱里的动物不会被任何敌方动物吃掉
|
|
if enemy[0] == 'r' and (targetRow,targetCol) in RED_TRAP_BLOCK:
|
|
return False
|
|
if enemy[0] == 'b' and (targetRow,targetCol) in BLACK_TRAP_BLOCK:
|
|
return False
|
|
# 敌方陷阱里的自家动物会被所有敌方动物吃掉
|
|
if enemy[0] == 'b' and (targetRow,targetCol) in RED_TRAP_BLOCK:
|
|
return True
|
|
if enemy[0] == 'r' and (targetRow,targetCol) in BLACK_TRAP_BLOCK:
|
|
return True
|
|
# 大象不能吃老鼠
|
|
if friend[1] == 'E' and enemy[1] == 'M':
|
|
return False
|
|
# 老鼠逻辑
|
|
if friend[1] == 'M':
|
|
# 在水里的老鼠可以吃水里的其他老鼠
|
|
if self.inWater(row, col)\
|
|
and self.inWater(targetRow, targetCol):
|
|
return True
|
|
# 在水里的老鼠不能吃大象
|
|
elif self.inWater(row, col)\
|
|
and not self.inWater(targetRow, targetCol):
|
|
return False
|
|
# 不在水里的老鼠不能吃水里的其他老鼠
|
|
elif not self.inWater(row, col)\
|
|
and self.inWater(targetRow, targetCol):
|
|
return False
|
|
# 不在水里的老鼠可以吃大象
|
|
elif enemy[1] == 'E':
|
|
return True
|
|
# 除此以外,按强度判断
|
|
elif ANIMAL_POWER[enemy[1]] <= ANIMAL_POWER[friend[1]]:
|
|
return True
|
|
# 其他动物逻辑:按强度判断
|
|
elif ANIMAL_POWER[enemy[1]] <= ANIMAL_POWER[friend[1]]:
|
|
return True
|
|
|
|
def mouseMove(self, row, col, moves):
|
|
''' 老鼠的移动方法 '''
|
|
directions = [(-1, 0), # 上
|
|
(0, -1), # 左
|
|
(1, 0), # 下
|
|
(0, 1)] # 右
|
|
enemyColor = "b" if self.red_to_move else "r"
|
|
|
|
for direction in directions:
|
|
# 获取目标位置
|
|
targetRow = row + direction[0] * 1
|
|
targetCol = col + direction[1] * 1
|
|
# 检查目标位置是否出界
|
|
if 0 <= targetRow < DIMENSION_ROW and 0 <= targetCol < DIMENSION_COL:
|
|
targetColor = self.board[targetRow][targetCol][0]
|
|
targetAnimal = self.board[targetRow][targetCol][1]
|
|
# 如果目标位置为空地/水地,且不是我方基地
|
|
if targetColor == "-" \
|
|
and not self.isFriendBase(targetRow, targetCol, enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
# 如果目标位置为敌方动物(判断能否吃掉)
|
|
elif targetColor == enemyColor \
|
|
and self.isEatable(row, col, targetRow, targetCol):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
def foxMove(self, row, col, moves):
|
|
''' 狐狸的移动方法 '''
|
|
directions = [(-1, 0), # 上
|
|
(-1, -1), # 左上
|
|
(0, -1), # 左
|
|
(1, -1), # 左下
|
|
(1, 0), # 下
|
|
(1, 1), # 右下
|
|
(0, 1), # 右
|
|
(-1, 1)] # 右上
|
|
enemyColor = "b" if self.red_to_move else "r"
|
|
|
|
for direction in directions:
|
|
# 获取目标位置
|
|
targetRow = row + direction[0] * 1
|
|
targetCol = col + direction[1] * 1
|
|
# 检查目标位置是否出界
|
|
if 0 <= targetRow < DIMENSION_ROW and 0 <= targetCol < DIMENSION_COL:
|
|
targetColor = self.board[targetRow][targetCol][0]
|
|
targetAnimal = self.board[targetRow][targetCol][1]
|
|
# 如果目标位置为陆空地,且不为水地,且不是我方基地(可以直接移动)
|
|
if targetColor == "-"\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and not self.isFriendBase(targetRow, targetCol, enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
# 如果目标位置有敌方动物(判断能否吃掉)
|
|
elif targetColor == enemyColor\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and self.isEatable(row, col, targetRow, targetCol):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
def birdMove(self, row, col, moves):
|
|
''' 老鹰的移动方法 '''
|
|
directions = [(-1, 0), # 上
|
|
(0, -1), # 左
|
|
(1, 0), # 下
|
|
(0, 1)] # 右
|
|
enemyColor = "b" if self.red_to_move else "r"
|
|
|
|
for direction in directions:
|
|
# 获取目标方向
|
|
targetRow = row + direction[0] * 1
|
|
targetCol = col + direction[1] * 1
|
|
|
|
possibleMovesInWater = []
|
|
|
|
# 检查目标位置是否出界 / 是否碰到棋子
|
|
while 0 <= targetRow < DIMENSION_ROW and 0 <= targetCol < DIMENSION_COL:
|
|
targetColor = self.board[targetRow][targetCol][0]
|
|
targetAnimal = self.board[targetRow][targetCol][1]
|
|
|
|
# 如果目标位置是陷阱,在陷阱上停下
|
|
if self.isEnemyTrap(targetRow, targetCol, enemyColor)\
|
|
or self.isFriendTrap(targetRow, targetCol, enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board) )
|
|
break
|
|
|
|
# 如果目标位置为陆空地,且不是我方基地(可以直接移动)
|
|
elif targetColor == "-"\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and not self.isFriendBase(targetRow, targetCol, enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
# 如果目标位置为水空地,可能可以移动
|
|
elif targetColor == "-"\
|
|
and self.inWater(targetRow, targetCol):
|
|
possibleMovesInWater.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
# 如果目标位置不是空地
|
|
elif targetColor != "-":
|
|
# 水中有动物,路径作废
|
|
if self.inWater(targetRow, targetCol):
|
|
possibleMovesInWater.clear()
|
|
break
|
|
# 陆地动物,做吃子判断
|
|
else:
|
|
# 如果为自家动物,停下
|
|
if targetColor != enemyColor:
|
|
break
|
|
# 如果能吃掉,则可以走到那一格,否则最多在其前面停下
|
|
if self.isEatable(row, col, targetRow, targetCol):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
break
|
|
|
|
targetRow += direction[0]
|
|
targetCol += direction[1]
|
|
|
|
moves.extend(possibleMovesInWater)
|
|
|
|
def lionMove(self, row, col, moves):
|
|
''' 狮子的移动方法 '''
|
|
directions = [(-1, 0), # 上
|
|
(0, -1), # 左
|
|
(1, 0), # 下
|
|
(0, 1)] # 右
|
|
enemyColor = "b" if self.red_to_move else "r"
|
|
|
|
for direction in directions:
|
|
# 获取目标位置
|
|
targetRow = row + direction[0] * 1
|
|
targetCol = col + direction[1] * 1
|
|
# 检查目标位置是否出界
|
|
if 0 <= targetRow < DIMENSION_ROW and 0 <= targetCol < DIMENSION_COL:
|
|
targetColor = self.board[targetRow][targetCol][0]
|
|
targetAnimal = self.board[targetRow][targetCol][1]
|
|
# 如果目标位置为陆空地,且不是我方基地(可以直接移动)
|
|
if targetColor == "-"\
|
|
and not self.inWater(targetRow,targetCol)\
|
|
and not self.isFriendBase(targetRow,targetCol,enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
# 如果目标位置在水中(跳河逻辑)
|
|
elif targetColor == "-" and self.inWater(targetRow,targetCol):
|
|
# 确定跳跃方向
|
|
jumpDirectionRow = targetRow - row
|
|
jumpDirectionCol = targetCol - col
|
|
# 判断是否满足纵向跳河条件
|
|
if jumpDirectionRow != 0\
|
|
and self.jumpConditions(row,col,targetRow,targetCol,jumpDirectionRow,jumpDirectionCol,enemyColor):
|
|
moves.append(Move(row, col, targetRow + (WATER_HEIGHT * jumpDirectionRow), targetCol, self.board))
|
|
# 判断是否满足横向跳河条件
|
|
elif jumpDirectionCol != 0\
|
|
and self.jumpConditions(row,col,targetRow,targetCol,jumpDirectionRow,jumpDirectionCol,enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol + (WATER_WIDTH * jumpDirectionCol), self.board))
|
|
# 如果目标位置为草地,有敌方动物(判断能否吃掉)
|
|
elif targetColor == enemyColor\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and self.isEatable(row, col, targetRow, targetCol):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
def standardMove(self, row, col, moves):
|
|
''' 其他动物的移动方法 '''
|
|
directions = [(-1, 0), # 上
|
|
(0, -1), # 左
|
|
(1, 0), # 下
|
|
(0, 1)] # 右
|
|
enemyColor = "b" if self.red_to_move else "r"
|
|
|
|
for direction in directions:
|
|
# 获取目标位置
|
|
targetRow = row + direction[0] * 1
|
|
targetCol = col + direction[1] * 1
|
|
# 检查目标位置是否出界
|
|
if 0 <= targetRow < DIMENSION_ROW and 0 <= targetCol < DIMENSION_COL:
|
|
targetColor = self.board[targetRow][targetCol][0]
|
|
targetAnimal = self.board[targetRow][targetCol][1]
|
|
# 如果目标位置为陆空地,且不是我方基地(可以直接移动)
|
|
if targetColor == "-"\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and not self.isFriendBase(targetRow, targetCol, enemyColor):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
# 如果目标位置为草地,有敌方动物(判断能否吃掉)
|
|
elif targetColor == enemyColor\
|
|
and not self.inWater(targetRow, targetCol)\
|
|
and self.isEatable(row, col, targetRow, targetCol):
|
|
moves.append(Move(row, col, targetRow, targetCol, self.board))
|
|
|
|
|
|
|
|
class Move(object):
|
|
''' 表示每一步的决策 '''
|
|
def __init__(self, nowRow, nowCol, targetRow, targetCol, board):
|
|
self.nowRow = nowRow
|
|
self.nowCol = nowCol
|
|
self.targetRow = targetRow
|
|
self.targetCol = targetCol
|
|
self.nowStat = board[self.nowRow][self.nowCol]
|
|
self.targetStat = board[self.targetRow][self.targetCol]
|
|
self.isTargetEaten = self.targetStat[0] != "-" # 判定是否属于吃子情形
|
|
self.moveID = self.nowRow * 1000 + self.nowCol * 100 + self.targetRow * 10 + self.targetCol
|
|
|
|
def __repr__(self):
|
|
return f"{self.nowStat[1]}{self.nowRow}{self.nowCol}\
|
|
{self.targetStat}{self.targetRow}{self.targetCol}"
|
|
|
|
def __str__(self):
|
|
return ANIMAL_NAME[self.nowStat[1]] + f"({self.nowRow}, {self.nowCol})"\
|
|
+ f" -> ({self.targetRow}, {self.targetCol})"
|
|
|
|
def __eq__(self, other):
|
|
return self.moveID == other.moveID if isinstance(other, Move)\
|
|
else False
|
|
|
|
def __hash__(self):
|
|
return hash(self.__repr__()) |