更新为四人版本
This commit is contained in:
parent
a24ac2590a
commit
ed080b3f39
|
@ -7,21 +7,36 @@ from collections import Counter
|
||||||
Card2Column = {3: 0, 4: 1, 5: 2, 6: 3, 7: 4, 8: 5, 9: 6, 10: 7,
|
Card2Column = {3: 0, 4: 1, 5: 2, 6: 3, 7: 4, 8: 5, 9: 6, 10: 7,
|
||||||
11: 8, 12: 9, 13: 10, 14: 11, 17: 12, 20: 13, 30: 14}
|
11: 8, 12: 9, 13: 10, 14: 11, 17: 12, 20: 13, 30: 14}
|
||||||
|
|
||||||
NumOnes2Array = {0: np.array([0, 0, 0, 0]),
|
NumOnes2Array = {0: np.array([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
1: np.array([1, 0, 0, 0]),
|
1: np.array([1, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
2: np.array([1, 1, 0, 0]),
|
2: np.array([1, 1, 0, 0, 0, 0, 0, 0]),
|
||||||
3: np.array([1, 1, 1, 0]),
|
3: np.array([1, 1, 1, 0, 0, 0, 0, 0]),
|
||||||
4: np.array([1, 1, 1, 1])}
|
4: np.array([1, 1, 1, 1, 0, 0, 0, 0]),
|
||||||
|
5: np.array([1, 1, 1, 1, 1, 0, 0, 0]),
|
||||||
|
6: np.array([1, 1, 1, 1, 1, 1, 0, 0]),
|
||||||
|
7: np.array([1, 1, 1, 1, 1, 1, 1, 0]),
|
||||||
|
8: np.array([1, 1, 1, 1, 1, 1, 1, 1])}
|
||||||
|
|
||||||
def _get_one_hot_bomb(bomb_num):
|
|
||||||
one_hot = np.zeros(15, dtype=np.float32)
|
def _get_one_hot_bomb(bomb_num, use_legacy = False):
|
||||||
one_hot[bomb_num] = 1
|
"""
|
||||||
|
A utility function to encode the number of bombs
|
||||||
|
into one-hot representation.
|
||||||
|
"""
|
||||||
|
if use_legacy:
|
||||||
|
one_hot = np.zeros(29)
|
||||||
|
one_hot[bomb_num[0] + bomb_num[1]] = 1
|
||||||
|
else:
|
||||||
|
one_hot = np.zeros(56) # 14 + 15 + 27
|
||||||
|
one_hot[bomb_num[0]] = 1
|
||||||
|
one_hot[14 + bomb_num[1]] = 1
|
||||||
|
one_hot[29 + bomb_num[2]] = 1
|
||||||
return one_hot
|
return one_hot
|
||||||
|
|
||||||
def _load_model(position, model_dir, use_onnx):
|
def _load_model(position, model_dir, use_onnx):
|
||||||
if not use_onnx or not os.path.isfile(os.path.join(model_dir, position+'.onnx')) :
|
if not use_onnx or not os.path.isfile(os.path.join(model_dir, position+'.onnx')) :
|
||||||
from models import model_dict
|
from models import model_dict_new
|
||||||
model = model_dict[position]()
|
model = model_dict_new[position]()
|
||||||
model_state_dict = model.state_dict()
|
model_state_dict = model.state_dict()
|
||||||
model_path = os.path.join(model_dir, position+'.ckpt')
|
model_path = os.path.join(model_dir, position+'.ckpt')
|
||||||
if torch.cuda.is_available():
|
if torch.cuda.is_available():
|
||||||
|
@ -58,8 +73,15 @@ def _load_model(position, model_dir, use_onnx):
|
||||||
model = onnxruntime.InferenceSession(os.path.join(model_dir, position+'.onnx'))
|
model = onnxruntime.InferenceSession(os.path.join(model_dir, position+'.onnx'))
|
||||||
return model
|
return model
|
||||||
|
|
||||||
def _process_action_seq(sequence, length=15):
|
def _process_action_seq(sequence, length=20, new_model=True):
|
||||||
|
"""
|
||||||
|
A utility function encoding historical moves. We
|
||||||
|
encode 20 moves. If there is no 20 moves, we pad
|
||||||
|
with zeros.
|
||||||
|
"""
|
||||||
sequence = sequence[-length:].copy()
|
sequence = sequence[-length:].copy()
|
||||||
|
if new_model:
|
||||||
|
sequence = sequence[::-1]
|
||||||
if len(sequence) < length:
|
if len(sequence) < length:
|
||||||
empty_sequence = [[] for _ in range(length - len(sequence))]
|
empty_sequence = [[] for _ in range(length - len(sequence))]
|
||||||
empty_sequence.extend(sequence)
|
empty_sequence.extend(sequence)
|
||||||
|
@ -74,18 +96,22 @@ class DeepAgent:
|
||||||
|
|
||||||
def cards2array(self, list_cards):
|
def cards2array(self, list_cards):
|
||||||
if len(list_cards) == 0:
|
if len(list_cards) == 0:
|
||||||
return np.zeros(54, dtype=np.float32)
|
return np.zeros(108, dtype=np.int8)
|
||||||
|
|
||||||
matrix = np.zeros([4, 13], dtype=np.float32)
|
matrix = np.zeros([8, 13], dtype=np.int8)
|
||||||
jokers = np.zeros(2, dtype=np.float32)
|
jokers = np.zeros(4, dtype=np.int8)
|
||||||
counter = Counter(list_cards)
|
counter = Counter(list_cards)
|
||||||
for card, num_times in counter.items():
|
for card, num_times in counter.items():
|
||||||
if card < 20:
|
if card < 20:
|
||||||
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
||||||
elif card == 20:
|
elif card == 20:
|
||||||
jokers[0] = 1
|
jokers[0] = 1
|
||||||
elif card == 30:
|
if num_times == 2:
|
||||||
jokers[1] = 1
|
jokers[1] = 1
|
||||||
|
elif card == 30:
|
||||||
|
jokers[2] = 1
|
||||||
|
if num_times == 2:
|
||||||
|
jokers[3] = 1
|
||||||
return np.concatenate((matrix.flatten('F'), jokers))
|
return np.concatenate((matrix.flatten('F'), jokers))
|
||||||
|
|
||||||
def get_one_hot_array(self, num_left_cards, max_num_cards):
|
def get_one_hot_array(self, num_left_cards, max_num_cards):
|
||||||
|
@ -94,162 +120,135 @@ class DeepAgent:
|
||||||
|
|
||||||
return one_hot
|
return one_hot
|
||||||
|
|
||||||
def action_seq_list2array(self, action_seq_list):
|
def action_seq_list2array(self, action_seq_list, new_model=True):
|
||||||
action_seq_array = np.zeros((len(action_seq_list), 54), dtype=np.float32)
|
"""
|
||||||
|
A utility function to encode the historical moves.
|
||||||
|
We encode the historical 20 actions. If there is
|
||||||
|
no 20 actions, we pad the features with 0. Since
|
||||||
|
three moves is a round in DouDizhu, we concatenate
|
||||||
|
the representations for each consecutive three moves.
|
||||||
|
Finally, we obtain a 5x432 matrix, which will be fed
|
||||||
|
into LSTM for encoding.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if new_model:
|
||||||
|
# position_map = {"landlord": 0, "landlord_up": 1, "landlord_front": 2, "landlord_down": 3}
|
||||||
|
action_seq_array = np.ones((len(action_seq_list), 108)) * -1 # Default Value -1 for not using area
|
||||||
for row, list_cards in enumerate(action_seq_list):
|
for row, list_cards in enumerate(action_seq_list):
|
||||||
|
if list_cards != []:
|
||||||
|
action_seq_array[row, :108] = self.cards2array(list_cards)
|
||||||
|
else:
|
||||||
|
action_seq_array = np.zeros((len(action_seq_list), 108))
|
||||||
|
for row, list_cards in enumerate(action_seq_list):
|
||||||
|
if list_cards != []:
|
||||||
action_seq_array[row, :] = self.cards2array(list_cards)
|
action_seq_array[row, :] = self.cards2array(list_cards)
|
||||||
action_seq_array = action_seq_array.reshape(5, 162)
|
action_seq_array = action_seq_array.reshape(5, 432)
|
||||||
return action_seq_array
|
return action_seq_array
|
||||||
|
|
||||||
def act(self, infoset):
|
def get_obs_general(self, infoset, position, use_legacy = False):
|
||||||
player_position = infoset.player_position
|
|
||||||
num_legal_actions = len(infoset.legal_actions)
|
num_legal_actions = len(infoset.legal_actions)
|
||||||
my_handcards = self.cards2array(infoset.player_hand_cards)
|
my_handcards = self.cards2array(infoset.player_hand_cards)
|
||||||
my_handcards_batch = np.repeat(my_handcards[np.newaxis, :],
|
my_handcards_batch = np.repeat(my_handcards[np.newaxis, :],
|
||||||
num_legal_actions, axis=0)
|
num_legal_actions, axis=0)
|
||||||
|
|
||||||
other_handcards = self.cards2array(infoset.other_hand_cards)
|
other_handcards = self.cards2array(infoset.other_hand_cards)
|
||||||
other_handcards_batch = np.repeat(other_handcards[np.newaxis, :],
|
|
||||||
|
bid_info = np.array(infoset.bid_info).flatten()
|
||||||
|
bid_info_batch = np.repeat(bid_info[np.newaxis, :],
|
||||||
num_legal_actions, axis=0)
|
num_legal_actions, axis=0)
|
||||||
|
|
||||||
my_action_batch = np.zeros(my_handcards_batch.shape, dtype=np.float32)
|
multiply_info = np.array(infoset.multiply_info)
|
||||||
|
multiply_info_batch = np.repeat(multiply_info[np.newaxis, :],
|
||||||
|
num_legal_actions, axis=0)
|
||||||
|
|
||||||
|
my_action_batch = np.zeros(my_handcards_batch.shape)
|
||||||
for j, action in enumerate(infoset.legal_actions):
|
for j, action in enumerate(infoset.legal_actions):
|
||||||
my_action_batch[j, :] = self.cards2array(action)
|
my_action_batch[j, :] = self.cards2array(action)
|
||||||
|
|
||||||
last_action = self.cards2array(infoset.rival_move)
|
landlord_num_cards_left = self.get_one_hot_array(
|
||||||
last_action_batch = np.repeat(last_action[np.newaxis, :],
|
infoset.num_cards_left_dict['landlord'], 33)
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
if player_position == 0:
|
|
||||||
landlord_up_num_cards_left = self.get_one_hot_array(
|
landlord_up_num_cards_left = self.get_one_hot_array(
|
||||||
infoset.num_cards_left[2], 17)
|
infoset.num_cards_left_dict['landlord_up'], 25)
|
||||||
landlord_up_num_cards_left_batch = np.repeat(
|
|
||||||
landlord_up_num_cards_left[np.newaxis, :],
|
landlord_front_num_cards_left = self.get_one_hot_array(
|
||||||
num_legal_actions, axis=0)
|
infoset.num_cards_left_dict['landlord_front'], 25)
|
||||||
|
|
||||||
landlord_down_num_cards_left = self.get_one_hot_array(
|
landlord_down_num_cards_left = self.get_one_hot_array(
|
||||||
infoset.num_cards_left[1], 17)
|
infoset.num_cards_left_dict['landlord_down'], 25)
|
||||||
landlord_down_num_cards_left_batch = np.repeat(
|
|
||||||
landlord_down_num_cards_left[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
landlord_up_played_cards = self.cards2array(
|
|
||||||
infoset.played_cards[2])
|
|
||||||
landlord_up_played_cards_batch = np.repeat(
|
|
||||||
landlord_up_played_cards[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
landlord_down_played_cards = self.cards2array(
|
|
||||||
infoset.played_cards[1])
|
|
||||||
landlord_down_played_cards_batch = np.repeat(
|
|
||||||
landlord_down_played_cards[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
bomb_num = _get_one_hot_bomb(
|
|
||||||
infoset.bomb_num)
|
|
||||||
bomb_num_batch = np.repeat(
|
|
||||||
bomb_num[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
x_batch = np.hstack((my_handcards_batch,
|
|
||||||
other_handcards_batch,
|
|
||||||
last_action_batch,
|
|
||||||
landlord_up_played_cards_batch,
|
|
||||||
landlord_down_played_cards_batch,
|
|
||||||
landlord_up_num_cards_left_batch,
|
|
||||||
landlord_down_num_cards_left_batch,
|
|
||||||
bomb_num_batch,
|
|
||||||
my_action_batch))
|
|
||||||
z = self.action_seq_list2array(_process_action_seq(
|
|
||||||
infoset.card_play_action_seq))
|
|
||||||
z_batch = np.repeat(
|
|
||||||
z[np.newaxis, :, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
if self.use_onnx:
|
|
||||||
ort_inputs = {'z': z_batch, 'x': x_batch}
|
|
||||||
y_pred = self.model.run(None, ort_inputs)[0]
|
|
||||||
elif torch.cuda.is_available():
|
|
||||||
y_pred = self.model.forward(torch.from_numpy(z_batch).float().cuda(),
|
|
||||||
torch.from_numpy(x_batch).float().cuda())
|
|
||||||
y_pred = y_pred.cpu().detach().numpy()
|
|
||||||
else:
|
|
||||||
y_pred = self.model.forward(torch.from_numpy(z_batch).float(),
|
|
||||||
torch.from_numpy(x_batch).float())
|
|
||||||
y_pred = y_pred.detach().numpy()
|
|
||||||
else:
|
|
||||||
last_landlord_action = self.cards2array(
|
|
||||||
infoset.last_moves[0])
|
|
||||||
last_landlord_action_batch = np.repeat(
|
|
||||||
last_landlord_action[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
landlord_num_cards_left = self.get_one_hot_array(
|
|
||||||
infoset.num_cards_left[0], 20)
|
|
||||||
landlord_num_cards_left_batch = np.repeat(
|
|
||||||
landlord_num_cards_left[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
landlord_played_cards = self.cards2array(
|
landlord_played_cards = self.cards2array(
|
||||||
infoset.played_cards[0])
|
infoset.played_cards[0])
|
||||||
landlord_played_cards_batch = np.repeat(
|
|
||||||
landlord_played_cards[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
if player_position == 2:
|
landlord_up_played_cards = self.cards2array(
|
||||||
last_teammate_action = self.cards2array(
|
|
||||||
infoset.last_moves[1])
|
|
||||||
last_teammate_action_batch = np.repeat(
|
|
||||||
last_teammate_action[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
teammate_num_cards_left = self.get_one_hot_array(
|
|
||||||
infoset.num_cards_left[1], 17)
|
|
||||||
teammate_num_cards_left_batch = np.repeat(
|
|
||||||
teammate_num_cards_left[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
teammate_played_cards = self.cards2array(
|
|
||||||
infoset.played_cards[1])
|
infoset.played_cards[1])
|
||||||
teammate_played_cards_batch = np.repeat(
|
|
||||||
teammate_played_cards[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
else:
|
landlord_front_played_cards = self.cards2array(
|
||||||
last_teammate_action = self.cards2array(
|
|
||||||
infoset.last_moves[2])
|
|
||||||
last_teammate_action_batch = np.repeat(
|
|
||||||
last_teammate_action[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
teammate_num_cards_left = self.get_one_hot_array(
|
|
||||||
infoset.num_cards_left[2], 17)
|
|
||||||
teammate_num_cards_left_batch = np.repeat(
|
|
||||||
teammate_num_cards_left[np.newaxis, :],
|
|
||||||
num_legal_actions, axis=0)
|
|
||||||
|
|
||||||
teammate_played_cards = self.cards2array(
|
|
||||||
infoset.played_cards[2])
|
infoset.played_cards[2])
|
||||||
teammate_played_cards_batch = np.repeat(
|
|
||||||
teammate_played_cards[np.newaxis, :],
|
landlord_down_played_cards = self.cards2array(
|
||||||
num_legal_actions, axis=0)
|
infoset.played_cards[3])
|
||||||
|
|
||||||
bomb_num = _get_one_hot_bomb(
|
bomb_num = _get_one_hot_bomb(
|
||||||
infoset.bomb_num)
|
infoset.bomb_num, use_legacy)
|
||||||
bomb_num_batch = np.repeat(
|
bomb_num_batch = np.repeat(
|
||||||
bomb_num[np.newaxis, :],
|
bomb_num[np.newaxis, :],
|
||||||
num_legal_actions, axis=0)
|
num_legal_actions, axis=0)
|
||||||
x_batch = np.hstack((my_handcards_batch,
|
num_cards_left = np.hstack((
|
||||||
other_handcards_batch,
|
landlord_num_cards_left, # 33
|
||||||
landlord_played_cards_batch,
|
landlord_up_num_cards_left, # 25
|
||||||
teammate_played_cards_batch,
|
landlord_front_num_cards_left, # 25
|
||||||
last_action_batch,
|
landlord_down_num_cards_left))
|
||||||
last_landlord_action_batch,
|
|
||||||
last_teammate_action_batch,
|
if use_legacy:
|
||||||
landlord_num_cards_left_batch,
|
x_batch = np.hstack((
|
||||||
teammate_num_cards_left_batch,
|
bid_info_batch, # 20
|
||||||
bomb_num_batch,
|
multiply_info_batch)) # 4
|
||||||
my_action_batch))
|
x_no_action = np.hstack((
|
||||||
z = self.action_seq_list2array(_process_action_seq(infoset.card_play_action_seq))
|
bid_info,
|
||||||
z_batch = np.repeat(
|
multiply_info))
|
||||||
|
else:
|
||||||
|
x_batch = np.hstack((
|
||||||
|
bomb_num_batch, # 56
|
||||||
|
bid_info_batch, # 20
|
||||||
|
multiply_info_batch)) # 4
|
||||||
|
x_no_action = np.hstack((
|
||||||
|
bomb_num, # 56
|
||||||
|
bid_info,
|
||||||
|
multiply_info))
|
||||||
|
z =np.vstack((
|
||||||
|
num_cards_left,
|
||||||
|
my_handcards, # 108
|
||||||
|
other_handcards, # 108
|
||||||
|
# three_landlord_cards, # 108
|
||||||
|
landlord_played_cards, # 108
|
||||||
|
landlord_up_played_cards, # 108
|
||||||
|
landlord_front_played_cards, # 108
|
||||||
|
landlord_down_played_cards, # 108
|
||||||
|
self.action_seq_list2array(_process_action_seq(infoset.card_play_action_seq, 32))
|
||||||
|
))
|
||||||
|
|
||||||
|
_z_batch = np.repeat(
|
||||||
z[np.newaxis, :, :],
|
z[np.newaxis, :, :],
|
||||||
num_legal_actions, axis=0)
|
num_legal_actions, axis=0)
|
||||||
|
my_action_batch = my_action_batch[:,np.newaxis,:]
|
||||||
|
z_batch = np.zeros([len(_z_batch),40,108],int)
|
||||||
|
for i in range(0,len(_z_batch)):
|
||||||
|
z_batch[i] = np.vstack((my_action_batch[i],_z_batch[i]))
|
||||||
|
obs = {
|
||||||
|
'position': position,
|
||||||
|
'x_batch': x_batch.astype(np.float32),
|
||||||
|
'z_batch': z_batch.astype(np.float32),
|
||||||
|
'legal_actions': infoset.legal_actions,
|
||||||
|
'x_no_action': x_no_action.astype(np.int8),
|
||||||
|
'z': z.astype(np.int8),
|
||||||
|
}
|
||||||
|
return obs
|
||||||
|
|
||||||
|
def act(self, infoset):
|
||||||
|
obs = self.get_obs_general(infoset, infoset.player_position)
|
||||||
|
z_batch = obs['z_batch']
|
||||||
|
x_batch = obs['x_batch']
|
||||||
if self.use_onnx:
|
if self.use_onnx:
|
||||||
ort_inputs = {'z': z_batch, 'x': x_batch}
|
ort_inputs = {'z': z_batch, 'x': x_batch}
|
||||||
y_pred = self.model.run(None, ort_inputs)[0]
|
y_pred = self.model.run(None, ort_inputs)[0]
|
||||||
|
@ -259,7 +258,7 @@ class DeepAgent:
|
||||||
y_pred = y_pred.cpu().detach().numpy()
|
y_pred = y_pred.cpu().detach().numpy()
|
||||||
else:
|
else:
|
||||||
y_pred = self.model.forward(torch.from_numpy(z_batch).float(),
|
y_pred = self.model.forward(torch.from_numpy(z_batch).float(),
|
||||||
torch.from_numpy(x_batch).float())
|
torch.from_numpy(x_batch).float())['values']
|
||||||
y_pred = y_pred.detach().numpy()
|
y_pred = y_pred.detach().numpy()
|
||||||
|
|
||||||
y_pred = y_pred.flatten()
|
y_pred = y_pred.flatten()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import torch
|
import torch
|
||||||
from torch import nn
|
from torch import nn
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
class LandlordLstmModel(nn.Module):
|
class LandlordLstmModel(nn.Module):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -61,7 +62,137 @@ class FarmerLstmModel(nn.Module):
|
||||||
x = self.dense6(x)
|
x = self.dense6(x)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
# 用于ResNet18和34的残差块,用的是2个3x3的卷积
|
||||||
|
class BasicBlock(nn.Module):
|
||||||
|
expansion = 1
|
||||||
|
|
||||||
|
def __init__(self, in_planes, planes, stride=1):
|
||||||
|
super(BasicBlock, self).__init__()
|
||||||
|
self.conv1 = nn.Conv1d(in_planes, planes, kernel_size=(3,),
|
||||||
|
stride=(stride,), padding=1, bias=False)
|
||||||
|
self.bn1 = nn.BatchNorm1d(planes)
|
||||||
|
self.conv2 = nn.Conv1d(planes, planes, kernel_size=(3,),
|
||||||
|
stride=(1,), padding=1, bias=False)
|
||||||
|
self.bn2 = nn.BatchNorm1d(planes)
|
||||||
|
self.shortcut = nn.Sequential()
|
||||||
|
# 经过处理后的x要与x的维度相同(尺寸和深度)
|
||||||
|
# 如果不相同,需要添加卷积+BN来变换为同一维度
|
||||||
|
if stride != 1 or in_planes != self.expansion * planes:
|
||||||
|
self.shortcut = nn.Sequential(
|
||||||
|
nn.Conv1d(in_planes, self.expansion * planes,
|
||||||
|
kernel_size=(1,), stride=(stride,), bias=False),
|
||||||
|
nn.BatchNorm1d(self.expansion * planes)
|
||||||
|
)
|
||||||
|
|
||||||
|
def forward(self, x):
|
||||||
|
out = F.relu(self.bn1(self.conv1(x)))
|
||||||
|
out = self.bn2(self.conv2(out))
|
||||||
|
out += self.shortcut(x)
|
||||||
|
out = F.relu(out)
|
||||||
|
return out
|
||||||
|
|
||||||
|
class GeneralModel(nn.Module):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.in_planes = 80
|
||||||
|
#input 1*108*41
|
||||||
|
self.conv1 = nn.Conv1d(40, 80, kernel_size=(3,),
|
||||||
|
stride=(2,), padding=1, bias=False) #1*108*80
|
||||||
|
|
||||||
|
self.bn1 = nn.BatchNorm1d(80)
|
||||||
|
|
||||||
|
self.layer1 = self._make_layer(BasicBlock, 80, 2, stride=2)#1*27*80
|
||||||
|
self.layer2 = self._make_layer(BasicBlock, 160, 2, stride=2)#1*14*160
|
||||||
|
self.layer3 = self._make_layer(BasicBlock, 320, 2, stride=2)#1*7*320
|
||||||
|
self.layer4 = self._make_layer(BasicBlock, 640, 2, stride=2)#1*4*640
|
||||||
|
# self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2)
|
||||||
|
self.linear1 = nn.Linear(640 * BasicBlock.expansion * 4 + 80, 2048)
|
||||||
|
self.linear2 = nn.Linear(2048, 1024)
|
||||||
|
self.linear3 = nn.Linear(1024, 512)
|
||||||
|
self.linear4 = nn.Linear(512, 256)
|
||||||
|
self.linear5 = nn.Linear(256, 1)
|
||||||
|
|
||||||
|
def _make_layer(self, block, planes, num_blocks, stride):
|
||||||
|
strides = [stride] + [1] * (num_blocks - 1)
|
||||||
|
layers = []
|
||||||
|
for stride in strides:
|
||||||
|
layers.append(block(self.in_planes, planes, stride))
|
||||||
|
self.in_planes = planes * block.expansion
|
||||||
|
return nn.Sequential(*layers)
|
||||||
|
|
||||||
|
def get_onnx_params(self):
|
||||||
|
return {
|
||||||
|
'args': (
|
||||||
|
torch.tensor(np.zeros([1, 40, 108]), dtype=torch.float32, device='cuda:0'),
|
||||||
|
torch.tensor(np.zeros((1, 80)), dtype=torch.float32, device='cuda:0')
|
||||||
|
),
|
||||||
|
'input_names': ['z_batch','x_batch'],
|
||||||
|
'output_names': ['values'],
|
||||||
|
'dynamic_axes': {
|
||||||
|
'z_batch': {
|
||||||
|
0: "legal_actions"
|
||||||
|
},
|
||||||
|
'x_batch': {
|
||||||
|
0: "legal_actions"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def forward(self, z, x):
|
||||||
|
out = F.relu(self.bn1(self.conv1(z)))
|
||||||
|
out = self.layer1(out)
|
||||||
|
out = self.layer2(out)
|
||||||
|
out = self.layer3(out)
|
||||||
|
out = self.layer4(out)
|
||||||
|
out = out.flatten(1,2)
|
||||||
|
out = torch.cat([x,out], dim=-1)
|
||||||
|
out = F.leaky_relu_(self.linear1(out))
|
||||||
|
out = F.leaky_relu_(self.linear2(out))
|
||||||
|
out = F.leaky_relu_(self.linear3(out))
|
||||||
|
out = F.leaky_relu_(self.linear4(out))
|
||||||
|
out = F.leaky_relu_(self.linear5(out))
|
||||||
|
return dict(values=out)
|
||||||
|
|
||||||
|
|
||||||
|
class BidModel(nn.Module):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
self.dense1 = nn.Linear(208, 512)
|
||||||
|
self.dense2 = nn.Linear(512, 512)
|
||||||
|
self.dense3 = nn.Linear(512, 512)
|
||||||
|
self.dense4 = nn.Linear(512, 512)
|
||||||
|
self.dense5 = nn.Linear(512, 512)
|
||||||
|
self.dense6 = nn.Linear(512, 1)
|
||||||
|
|
||||||
|
def forward(self, z, x):
|
||||||
|
x = self.dense1(x)
|
||||||
|
x = F.leaky_relu(x)
|
||||||
|
# x = F.relu(x)
|
||||||
|
x = self.dense2(x)
|
||||||
|
x = F.leaky_relu(x)
|
||||||
|
# x = F.relu(x)
|
||||||
|
x = self.dense3(x)
|
||||||
|
x = F.leaky_relu(x)
|
||||||
|
# x = F.relu(x)
|
||||||
|
x = self.dense4(x)
|
||||||
|
x = F.leaky_relu(x)
|
||||||
|
# x = F.relu(x)
|
||||||
|
x = self.dense5(x)
|
||||||
|
# x = F.relu(x)
|
||||||
|
x = F.leaky_relu(x)
|
||||||
|
x = self.dense6(x)
|
||||||
|
return dict(values=x)
|
||||||
|
|
||||||
|
|
||||||
model_dict = {}
|
model_dict = {}
|
||||||
model_dict['landlord'] = LandlordLstmModel
|
model_dict['landlord'] = LandlordLstmModel
|
||||||
model_dict['landlord_up'] = FarmerLstmModel
|
model_dict['landlord_up'] = FarmerLstmModel
|
||||||
model_dict['landlord_down'] = FarmerLstmModel
|
model_dict['landlord_down'] = FarmerLstmModel
|
||||||
|
model_dict_new = {}
|
||||||
|
model_dict_new['landlord'] = GeneralModel
|
||||||
|
model_dict_new['landlord_up'] = GeneralModel
|
||||||
|
model_dict_new['landlord_front'] = GeneralModel
|
||||||
|
model_dict_new['landlord_down'] = GeneralModel
|
||||||
|
model_dict_new['bidding'] = BidModel
|
||||||
|
|
|
@ -37,7 +37,7 @@ RealCard2EnvCard = {'3': '3', '4': '4', '5': '5', '6': '6', '7': '7',
|
||||||
pretrained_dir = 'pretrained/dmc_pretrained'
|
pretrained_dir = 'pretrained/dmc_pretrained'
|
||||||
device = torch.device('cpu')
|
device = torch.device('cpu')
|
||||||
players = []
|
players = []
|
||||||
for i in range(3):
|
for i in range(4):
|
||||||
model_path = os.path.join(pretrained_dir, str(i)+'.pth')
|
model_path = os.path.join(pretrained_dir, str(i)+'.pth')
|
||||||
agent = torch.load(model_path, map_location=device)
|
agent = torch.load(model_path, map_location=device)
|
||||||
agent.set_device(device)
|
agent.set_device(device)
|
||||||
|
@ -50,33 +50,38 @@ def predict():
|
||||||
try:
|
try:
|
||||||
# Player postion
|
# Player postion
|
||||||
player_position = request.form.get('player_position')
|
player_position = request.form.get('player_position')
|
||||||
if player_position not in ['0', '1', '2']:
|
if player_position not in ['0', '1', '2', '3']:
|
||||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, or 2'})
|
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, 2 or 3'})
|
||||||
player_position = int(player_position)
|
player_position = int(player_position)
|
||||||
|
|
||||||
# Player hand cards
|
# Player hand cards
|
||||||
player_hand_cards = ''.join(
|
player_hand_cards = ''.join(
|
||||||
[RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')])
|
[RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')])
|
||||||
if player_position == 0:
|
if player_position == 0:
|
||||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 20:
|
if len(player_hand_cards) < 1 or len(player_hand_cards) > 33:
|
||||||
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-20'})
|
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-33'})
|
||||||
else:
|
else:
|
||||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 17:
|
if len(player_hand_cards) < 1 or len(player_hand_cards) > 25:
|
||||||
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-17'})
|
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-25'})
|
||||||
|
|
||||||
# Number cards left
|
# Number cards left
|
||||||
num_cards_left = [int(request.form.get('num_cards_left_landlord')), int(request.form.get(
|
num_cards_left = [
|
||||||
'num_cards_left_landlord_down')), int(request.form.get('num_cards_left_landlord_up'))]
|
int(request.form.get('num_cards_left_landlord')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_down')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_front')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_up'))
|
||||||
|
]
|
||||||
if num_cards_left[player_position] != len(player_hand_cards):
|
if num_cards_left[player_position] != len(player_hand_cards):
|
||||||
return jsonify({'status': 4, 'message': 'the number of cards left do not align with hand cards'})
|
return jsonify({'status': 4, 'message': 'the number of cards left do not align with hand cards'})
|
||||||
if num_cards_left[0] < 0 or num_cards_left[1] < 0 or num_cards_left[2] < 0 or num_cards_left[0] > 20 or num_cards_left[1] > 17 or num_cards_left[2] > 17:
|
if num_cards_left[0] < 0 or num_cards_left[1] < 0 or num_cards_left[2] < 0 or num_cards_left[3] < 0 \
|
||||||
|
or num_cards_left[0] > 33 or num_cards_left[1] > 25 or num_cards_left[2] > 25 or num_cards_left[3] > 25:
|
||||||
return jsonify({'status': 5, 'message': 'the number of cards left not in range'})
|
return jsonify({'status': 5, 'message': 'the number of cards left not in range'})
|
||||||
|
|
||||||
# Three landlord cards
|
# Three landlord cards
|
||||||
three_landlord_cards = ''.join(
|
# three_landlord_cards = ''.join(
|
||||||
[RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')])
|
# [RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')])
|
||||||
if len(three_landlord_cards) < 0 or len(three_landlord_cards) > 3:
|
# if len(three_landlord_cards) < 0 or len(three_landlord_cards) > 3:
|
||||||
return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
# return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
||||||
|
|
||||||
# Card play sequence
|
# Card play sequence
|
||||||
if request.form.get('card_play_action_seq') == '':
|
if request.form.get('card_play_action_seq') == '':
|
||||||
|
@ -89,7 +94,7 @@ def predict():
|
||||||
tmp_seq[i] = 'pass'
|
tmp_seq[i] = 'pass'
|
||||||
card_play_action_seq = []
|
card_play_action_seq = []
|
||||||
for i in range(len(tmp_seq)):
|
for i in range(len(tmp_seq)):
|
||||||
card_play_action_seq.append((i % 3, tmp_seq[i]))
|
card_play_action_seq.append((i % 4, tmp_seq[i]))
|
||||||
|
|
||||||
# Other hand cards
|
# Other hand cards
|
||||||
other_hand_cards = ''.join(
|
other_hand_cards = ''.join(
|
||||||
|
@ -99,7 +104,7 @@ def predict():
|
||||||
|
|
||||||
# Played cards
|
# Played cards
|
||||||
played_cards = []
|
played_cards = []
|
||||||
for field in ['played_cards_landlord', 'played_cards_landlord_down', 'played_cards_landlord_up']:
|
for field in ['played_cards_landlord', 'played_cards_landlord_down', 'played_cards_landlord_front', 'played_cards_landlord_up']:
|
||||||
played_cards.append(
|
played_cards.append(
|
||||||
''.join([RealCard2EnvCard[c] for c in request.form.get(field)]))
|
''.join([RealCard2EnvCard[c] for c in request.form.get(field)]))
|
||||||
|
|
||||||
|
@ -110,7 +115,7 @@ def predict():
|
||||||
state['num_cards_left'] = num_cards_left
|
state['num_cards_left'] = num_cards_left
|
||||||
state['others_hand'] = other_hand_cards
|
state['others_hand'] = other_hand_cards
|
||||||
state['played_cards'] = played_cards
|
state['played_cards'] = played_cards
|
||||||
state['seen_cards'] = three_landlord_cards
|
# state['seen_cards'] = three_landlord_cards
|
||||||
state['self'] = player_position
|
state['self'] = player_position
|
||||||
state['trace'] = card_play_action_seq
|
state['trace'] = card_play_action_seq
|
||||||
|
|
||||||
|
@ -118,6 +123,9 @@ def predict():
|
||||||
rival_move = 'pass'
|
rival_move = 'pass'
|
||||||
if len(card_play_action_seq) != 0:
|
if len(card_play_action_seq) != 0:
|
||||||
if card_play_action_seq[-1][1] == 'pass':
|
if card_play_action_seq[-1][1] == 'pass':
|
||||||
|
if card_play_action_seq[-2][1] == 'pass':
|
||||||
|
rival_move = card_play_action_seq[-3][1]
|
||||||
|
else:
|
||||||
rival_move = card_play_action_seq[-2][1]
|
rival_move = card_play_action_seq[-2][1]
|
||||||
else:
|
else:
|
||||||
rival_move = card_play_action_seq[-1][1]
|
rival_move = card_play_action_seq[-1][1]
|
||||||
|
@ -300,16 +308,33 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
||||||
moves = ms.filter_type_3_triple(all_moves, rival_move)
|
moves = ms.filter_type_3_triple(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_4_BOMB:
|
elif rival_move_type == md.TYPE_4_BOMB:
|
||||||
all_moves = mg.gen_type_4_bomb() + mg.gen_type_5_king_bomb()
|
all_moves = mg.gen_type_4_bomb(4)
|
||||||
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(5) + mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB5:
|
||||||
|
all_moves = mg.gen_type_4_bomb(5)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB6:
|
||||||
|
all_moves = mg.gen_type_4_bomb(6)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB7:
|
||||||
|
all_moves = mg.gen_type_4_bomb(7)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB8:
|
||||||
|
all_moves = mg.gen_type_4_bomb(8)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_5_KING_BOMB:
|
elif rival_move_type == md.TYPE_5_KING_BOMB:
|
||||||
moves = []
|
moves = []
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_6_3_1:
|
|
||||||
all_moves = mg.gen_type_6_3_1()
|
|
||||||
moves = ms.filter_type_6_3_1(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_7_3_2:
|
elif rival_move_type == md.TYPE_7_3_2:
|
||||||
all_moves = mg.gen_type_7_3_2()
|
all_moves = mg.gen_type_7_3_2()
|
||||||
moves = ms.filter_type_7_3_2(all_moves, rival_move)
|
moves = ms.filter_type_7_3_2(all_moves, rival_move)
|
||||||
|
@ -326,25 +351,12 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
||||||
all_moves = mg.gen_type_10_serial_triple(repeat_num=rival_move_len)
|
all_moves = mg.gen_type_10_serial_triple(repeat_num=rival_move_len)
|
||||||
moves = ms.filter_type_10_serial_triple(all_moves, rival_move)
|
moves = ms.filter_type_10_serial_triple(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_11_SERIAL_3_1:
|
|
||||||
all_moves = mg.gen_type_11_serial_3_1(repeat_num=rival_move_len)
|
|
||||||
moves = ms.filter_type_11_serial_3_1(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_12_SERIAL_3_2:
|
elif rival_move_type == md.TYPE_12_SERIAL_3_2:
|
||||||
all_moves = mg.gen_type_12_serial_3_2(repeat_num=rival_move_len)
|
all_moves = mg.gen_type_12_serial_3_2(repeat_num=rival_move_len)
|
||||||
moves = ms.filter_type_12_serial_3_2(all_moves, rival_move)
|
moves = ms.filter_type_12_serial_3_2(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_13_4_2:
|
if rival_move_type != md.TYPE_0_PASS and rival_move_type < md.TYPE_4_BOMB:
|
||||||
all_moves = mg.gen_type_13_4_2()
|
moves = moves + mg.gen_type_4_bomb(4) + mg.gen_type_4_bomb(5) + mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
moves = ms.filter_type_13_4_2(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_14_4_22:
|
|
||||||
all_moves = mg.gen_type_14_4_22()
|
|
||||||
moves = ms.filter_type_14_4_22(all_moves, rival_move)
|
|
||||||
|
|
||||||
if rival_move_type not in [md.TYPE_0_PASS,
|
|
||||||
md.TYPE_4_BOMB, md.TYPE_5_KING_BOMB]:
|
|
||||||
moves = moves + mg.gen_type_4_bomb() + mg.gen_type_5_king_bomb()
|
|
||||||
|
|
||||||
if len(rival_move) != 0: # rival_move is not 'pass'
|
if len(rival_move) != 0: # rival_move is not 'pass'
|
||||||
moves = moves + [[]]
|
moves = moves + [[]]
|
||||||
|
@ -355,37 +367,39 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
||||||
moves.sort()
|
moves.sort()
|
||||||
moves = list(move for move, _ in itertools.groupby(moves))
|
moves = list(move for move, _ in itertools.groupby(moves))
|
||||||
|
|
||||||
# Remove Quad with black and red joker
|
|
||||||
for i in [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17]:
|
|
||||||
illegal_move = [i]*4 + [20, 30]
|
|
||||||
if illegal_move in moves:
|
|
||||||
moves.remove(illegal_move)
|
|
||||||
|
|
||||||
return moves
|
return moves
|
||||||
|
|
||||||
|
|
||||||
Card2Column = {'3': 0, '4': 1, '5': 2, '6': 3, '7': 4, '8': 5, '9': 6, 'T': 7,
|
Card2Column = {'3': 0, '4': 1, '5': 2, '6': 3, '7': 4, '8': 5, '9': 6, 'T': 7,
|
||||||
'J': 8, 'Q': 9, 'K': 10, 'A': 11, '2': 12}
|
'J': 8, 'Q': 9, 'K': 10, 'A': 11, '2': 12}
|
||||||
|
|
||||||
NumOnes2Array = {0: np.array([0, 0, 0, 0]),
|
NumOnes2Array = {0: np.array([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
1: np.array([1, 0, 0, 0]),
|
1: np.array([1, 0, 0, 0, 0, 0, 0, 0]),
|
||||||
2: np.array([1, 1, 0, 0]),
|
2: np.array([1, 1, 0, 0, 0, 0, 0, 0]),
|
||||||
3: np.array([1, 1, 1, 0]),
|
3: np.array([1, 1, 1, 0, 0, 0, 0, 0]),
|
||||||
4: np.array([1, 1, 1, 1])}
|
4: np.array([1, 1, 1, 1, 0, 0, 0, 0]),
|
||||||
|
5: np.array([1, 1, 1, 1, 1, 0, 0, 0]),
|
||||||
|
6: np.array([1, 1, 1, 1, 1, 1, 0, 0]),
|
||||||
|
7: np.array([1, 1, 1, 1, 1, 1, 1, 0]),
|
||||||
|
8: np.array([1, 1, 1, 1, 1, 1, 1, 1])}
|
||||||
|
|
||||||
|
|
||||||
def _cards2array(cards):
|
def _cards2array(cards):
|
||||||
if cards == 'pass':
|
if cards == 'pass':
|
||||||
return np.zeros(54, dtype=np.int8)
|
return np.zeros(108, dtype=np.int8)
|
||||||
|
|
||||||
matrix = np.zeros([4, 13], dtype=np.int8)
|
matrix = np.zeros([4, 13], dtype=np.int8)
|
||||||
jokers = np.zeros(2, dtype=np.int8)
|
jokers = np.zeros(4, dtype=np.int8)
|
||||||
counter = Counter(cards)
|
counter = Counter(cards)
|
||||||
for card, num_times in counter.items():
|
for card, num_times in counter.items():
|
||||||
if card == 'B':
|
if card == 'B':
|
||||||
jokers[0] = 1
|
jokers[0] = 1
|
||||||
elif card == 'R':
|
if num_times == 2:
|
||||||
jokers[1] = 1
|
jokers[1] = 1
|
||||||
|
elif card == 'R':
|
||||||
|
jokers[2] = 1
|
||||||
|
if num_times == 2:
|
||||||
|
jokers[3] = 1
|
||||||
else:
|
else:
|
||||||
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
||||||
return np.concatenate((matrix.flatten('F'), jokers))
|
return np.concatenate((matrix.flatten('F'), jokers))
|
||||||
|
@ -399,10 +413,10 @@ def _get_one_hot_array(num_left_cards, max_num_cards):
|
||||||
|
|
||||||
|
|
||||||
def _action_seq2array(action_seq_list):
|
def _action_seq2array(action_seq_list):
|
||||||
action_seq_array = np.zeros((len(action_seq_list), 54), np.int8)
|
action_seq_array = np.ones((len(action_seq_list), 108)) * -1 # Default Value -1 for not using area
|
||||||
for row, cards in enumerate(action_seq_list):
|
for row, list_cards in enumerate(action_seq_list):
|
||||||
action_seq_array[row, :] = _cards2array(cards)
|
if list_cards != []:
|
||||||
action_seq_array = action_seq_array.flatten()
|
action_seq_array[row, :108] = _cards2array(list_cards[1])
|
||||||
return action_seq_array
|
return action_seq_array
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ RealCard2EnvCard = {'3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
|
||||||
|
|
||||||
pretrained_dir = 'pretrained/douzero_pretrained'
|
pretrained_dir = 'pretrained/douzero_pretrained'
|
||||||
players = []
|
players = []
|
||||||
for position in ['landlord', 'landlord_down', 'landlord_up']:
|
for position in ['landlord', 'landlord_down', 'landlord_front', 'landlord_up']:
|
||||||
players.append(DeepAgent(position, pretrained_dir, use_onnx=True))
|
players.append(DeepAgent(position, pretrained_dir, use_onnx=False))
|
||||||
|
|
||||||
@app.route('/predict', methods=['POST'])
|
@app.route('/predict', methods=['POST'])
|
||||||
def predict():
|
def predict():
|
||||||
|
@ -29,30 +29,36 @@ def predict():
|
||||||
try:
|
try:
|
||||||
# Player postion
|
# Player postion
|
||||||
player_position = request.form.get('player_position')
|
player_position = request.form.get('player_position')
|
||||||
if player_position not in ['0', '1', '2']:
|
if player_position not in ['0', '1', '2', '3']:
|
||||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, or 2'})
|
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, 2 or 3'})
|
||||||
player_position = int(player_position)
|
player_position = int(player_position)
|
||||||
|
|
||||||
# Player hand cards
|
# Player hand cards
|
||||||
player_hand_cards = [RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')]
|
player_hand_cards = [RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')]
|
||||||
if player_position == 0:
|
if player_position == 0:
|
||||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 20:
|
if len(player_hand_cards) < 1 or len(player_hand_cards) > 33:
|
||||||
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-20'})
|
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-33'})
|
||||||
else:
|
else:
|
||||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 17:
|
if len(player_hand_cards) < 1 or len(player_hand_cards) > 25:
|
||||||
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-17'})
|
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-25'})
|
||||||
|
|
||||||
# Number cards left
|
# Number cards left
|
||||||
num_cards_left = [int(request.form.get('num_cards_left_landlord')), int(request.form.get('num_cards_left_landlord_down')), int(request.form.get('num_cards_left_landlord_up'))]
|
num_cards_left = [
|
||||||
|
int(request.form.get('num_cards_left_landlord')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_down')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_front')),
|
||||||
|
int(request.form.get('num_cards_left_landlord_up'))
|
||||||
|
]
|
||||||
if num_cards_left[player_position] != len(player_hand_cards):
|
if num_cards_left[player_position] != len(player_hand_cards):
|
||||||
return jsonify({'status': 4, 'message': 'the number of cards left do not align with hand cards'})
|
return jsonify({'status': 4, 'message': 'the number of cards left do not align with hand cards'})
|
||||||
if num_cards_left[0] < 0 or num_cards_left[1] < 0 or num_cards_left[2] < 0 or num_cards_left[0] > 20 or num_cards_left[1] > 17 or num_cards_left[2] > 17:
|
if num_cards_left[0] < 0 or num_cards_left[1] < 0 or num_cards_left[2] < 0 or num_cards_left[3] < 0 \
|
||||||
|
or num_cards_left[0] > 33 or num_cards_left[1] > 25 or num_cards_left[2] > 25 or num_cards_left[2] > 25:
|
||||||
return jsonify({'status': 5, 'message': 'the number of cards left not in range'})
|
return jsonify({'status': 5, 'message': 'the number of cards left not in range'})
|
||||||
|
|
||||||
# Three landlord cards
|
# Three landlord cards
|
||||||
three_landlord_cards = [RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')]
|
# three_landlord_cards = [RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')]
|
||||||
if len(three_landlord_cards) < 0 or len(three_landlord_cards) > 3:
|
# if len(three_landlord_cards) < 0 or len(three_landlord_cards) > 3:
|
||||||
return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
# return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
||||||
|
|
||||||
# Card play sequence
|
# Card play sequence
|
||||||
if request.form.get('card_play_action_seq') == '':
|
if request.form.get('card_play_action_seq') == '':
|
||||||
|
@ -67,33 +73,46 @@ def predict():
|
||||||
|
|
||||||
# Last moves
|
# Last moves
|
||||||
last_moves = []
|
last_moves = []
|
||||||
for field in ['last_move_landlord', 'last_move_landlord_down', 'last_move_landlord_up']:
|
for field in ['last_move_landlord', 'last_move_landlord_down', 'last_move_landlord_front', 'last_move_landlord_up']:
|
||||||
last_moves.append([RealCard2EnvCard[c] for c in request.form.get(field)])
|
last_moves.append([RealCard2EnvCard[c] for c in request.form.get(field)])
|
||||||
|
|
||||||
# Played cards
|
# Played cards
|
||||||
played_cards = []
|
played_cards = []
|
||||||
for field in ['played_cards_landlord', 'played_cards_landlord_down', 'played_cards_landlord_up']:
|
for field in ['played_cards_landlord', 'played_cards_landlord_down', 'played_cards_landlord_front', 'played_cards_landlord_up']:
|
||||||
played_cards.append([RealCard2EnvCard[c] for c in request.form.get(field)])
|
played_cards.append([RealCard2EnvCard[c] for c in request.form.get(field)])
|
||||||
|
|
||||||
# Bomb Num
|
# Bomb Num
|
||||||
bomb_num = int(request.form.get('bomb_num'))
|
bomb_num = request.form.get('bomb_num')
|
||||||
|
|
||||||
# InfoSet
|
# InfoSet
|
||||||
info_set = InfoSet()
|
info_set = InfoSet()
|
||||||
info_set.player_position = player_position
|
info_set.player_position = player_position
|
||||||
info_set.player_hand_cards = player_hand_cards
|
info_set.player_hand_cards = player_hand_cards
|
||||||
info_set.num_cards_left = num_cards_left
|
info_set.num_cards_left = num_cards_left
|
||||||
info_set.three_landlord_cards = three_landlord_cards
|
# info_set.three_landlord_cards = three_landlord_cards
|
||||||
info_set.card_play_action_seq = card_play_action_seq
|
info_set.card_play_action_seq = card_play_action_seq
|
||||||
info_set.other_hand_cards = other_hand_cards
|
info_set.other_hand_cards = other_hand_cards
|
||||||
info_set.last_moves = last_moves
|
info_set.last_moves = last_moves
|
||||||
info_set.played_cards = played_cards
|
info_set.played_cards = played_cards
|
||||||
info_set.bomb_num = bomb_num
|
info_set.bomb_num = [int(x) for x in str.split(bomb_num, ',')]
|
||||||
|
info_set.bid_info = [[-1, -1, -1, -1],
|
||||||
|
[-1, -1, -1, -1],
|
||||||
|
[-1, -1, -1, -1],
|
||||||
|
[-1, -1, -1, -1],
|
||||||
|
[-1, -1, -1, -1]]
|
||||||
|
info_set.multiply_info = [1, 0, 0, 0]
|
||||||
|
info_set.num_cards_left_dict['landlord'] = num_cards_left[0]
|
||||||
|
info_set.num_cards_left_dict['landlord_down'] = num_cards_left[1]
|
||||||
|
info_set.num_cards_left_dict['landlord_front'] = num_cards_left[2]
|
||||||
|
info_set.num_cards_left_dict['landlord_up'] = num_cards_left[3]
|
||||||
|
|
||||||
# Get rival move and legal_actions
|
# Get rival move and legal_actions
|
||||||
rival_move = []
|
rival_move = []
|
||||||
if len(card_play_action_seq) != 0:
|
if len(card_play_action_seq) != 0:
|
||||||
if len(card_play_action_seq[-1]) == 0:
|
if len(card_play_action_seq[-1]) == 0:
|
||||||
|
if len(card_play_action_seq[-2]) == 0:
|
||||||
|
rival_move = card_play_action_seq[-3]
|
||||||
|
else:
|
||||||
rival_move = card_play_action_seq[-2]
|
rival_move = card_play_action_seq[-2]
|
||||||
else:
|
else:
|
||||||
rival_move = card_play_action_seq[-1]
|
rival_move = card_play_action_seq[-1]
|
||||||
|
@ -154,7 +173,9 @@ class InfoSet(object):
|
||||||
self.player_position = None
|
self.player_position = None
|
||||||
self.player_hand_cards = None
|
self.player_hand_cards = None
|
||||||
self.num_cards_left = None
|
self.num_cards_left = None
|
||||||
self.three_landlord_cards = None
|
self.num_cards_left_dict = {}
|
||||||
|
self.all_handcards = {}
|
||||||
|
# self.three_landlord_cards = None
|
||||||
self.card_play_action_seq = None
|
self.card_play_action_seq = None
|
||||||
self.other_hand_cards = None
|
self.other_hand_cards = None
|
||||||
self.legal_actions = None
|
self.legal_actions = None
|
||||||
|
@ -187,16 +208,33 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
||||||
moves = ms.filter_type_3_triple(all_moves, rival_move)
|
moves = ms.filter_type_3_triple(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_4_BOMB:
|
elif rival_move_type == md.TYPE_4_BOMB:
|
||||||
all_moves = mg.gen_type_4_bomb() + mg.gen_type_5_king_bomb()
|
all_moves = mg.gen_type_4_bomb(4)
|
||||||
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(5) + mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB5:
|
||||||
|
all_moves = mg.gen_type_4_bomb(5)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB6:
|
||||||
|
all_moves = mg.gen_type_4_bomb(6)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB7:
|
||||||
|
all_moves = mg.gen_type_4_bomb(7)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
|
elif rival_move_type == md.TYPE_4_BOMB8:
|
||||||
|
all_moves = mg.gen_type_4_bomb(8)
|
||||||
|
moves = ms.filter_type_4_bomb(all_moves, rival_move)
|
||||||
|
moves += mg.gen_type_5_king_bomb()
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_5_KING_BOMB:
|
elif rival_move_type == md.TYPE_5_KING_BOMB:
|
||||||
moves = []
|
moves = []
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_6_3_1:
|
|
||||||
all_moves = mg.gen_type_6_3_1()
|
|
||||||
moves = ms.filter_type_6_3_1(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_7_3_2:
|
elif rival_move_type == md.TYPE_7_3_2:
|
||||||
all_moves = mg.gen_type_7_3_2()
|
all_moves = mg.gen_type_7_3_2()
|
||||||
moves = ms.filter_type_7_3_2(all_moves, rival_move)
|
moves = ms.filter_type_7_3_2(all_moves, rival_move)
|
||||||
|
@ -213,25 +251,12 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
||||||
all_moves = mg.gen_type_10_serial_triple(repeat_num=rival_move_len)
|
all_moves = mg.gen_type_10_serial_triple(repeat_num=rival_move_len)
|
||||||
moves = ms.filter_type_10_serial_triple(all_moves, rival_move)
|
moves = ms.filter_type_10_serial_triple(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_11_SERIAL_3_1:
|
|
||||||
all_moves = mg.gen_type_11_serial_3_1(repeat_num=rival_move_len)
|
|
||||||
moves = ms.filter_type_11_serial_3_1(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_12_SERIAL_3_2:
|
elif rival_move_type == md.TYPE_12_SERIAL_3_2:
|
||||||
all_moves = mg.gen_type_12_serial_3_2(repeat_num=rival_move_len)
|
all_moves = mg.gen_type_12_serial_3_2(repeat_num=rival_move_len)
|
||||||
moves = ms.filter_type_12_serial_3_2(all_moves, rival_move)
|
moves = ms.filter_type_12_serial_3_2(all_moves, rival_move)
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_13_4_2:
|
if rival_move_type != md.TYPE_0_PASS and rival_move_type < md.TYPE_4_BOMB:
|
||||||
all_moves = mg.gen_type_13_4_2()
|
moves = moves + mg.gen_type_4_bomb(4) + mg.gen_type_4_bomb(5) + mg.gen_type_4_bomb(6) + mg.gen_type_4_bomb(7) + mg.gen_type_4_bomb(8) + mg.gen_type_5_king_bomb()
|
||||||
moves = ms.filter_type_13_4_2(all_moves, rival_move)
|
|
||||||
|
|
||||||
elif rival_move_type == md.TYPE_14_4_22:
|
|
||||||
all_moves = mg.gen_type_14_4_22()
|
|
||||||
moves = ms.filter_type_14_4_22(all_moves, rival_move)
|
|
||||||
|
|
||||||
if rival_move_type not in [md.TYPE_0_PASS,
|
|
||||||
md.TYPE_4_BOMB, md.TYPE_5_KING_BOMB]:
|
|
||||||
moves = moves + mg.gen_type_4_bomb() + mg.gen_type_5_king_bomb()
|
|
||||||
|
|
||||||
if len(rival_move) != 0: # rival_move is not 'pass'
|
if len(rival_move) != 0: # rival_move is not 'pass'
|
||||||
moves = moves + [[]]
|
moves = moves + [[]]
|
||||||
|
|
|
@ -24,8 +24,8 @@ def get_move_type(move):
|
||||||
if move_size == 2:
|
if move_size == 2:
|
||||||
if move[0] == move[1]:
|
if move[0] == move[1]:
|
||||||
return {'type': TYPE_2_PAIR, 'rank': move[0]}
|
return {'type': TYPE_2_PAIR, 'rank': move[0]}
|
||||||
elif move == [20, 30]: # Kings
|
# elif move == [20, 30]: # Kings
|
||||||
return {'type': TYPE_5_KING_BOMB}
|
# return {'type': TYPE_5_KING_BOMB}
|
||||||
else:
|
else:
|
||||||
return {'type': TYPE_15_WRONG}
|
return {'type': TYPE_15_WRONG}
|
||||||
|
|
||||||
|
@ -39,8 +39,10 @@ def get_move_type(move):
|
||||||
if len(move_dict) == 1:
|
if len(move_dict) == 1:
|
||||||
return {'type': TYPE_4_BOMB, 'rank': move[0]}
|
return {'type': TYPE_4_BOMB, 'rank': move[0]}
|
||||||
elif len(move_dict) == 2:
|
elif len(move_dict) == 2:
|
||||||
if move[0] == move[1] == move[2] or move[1] == move[2] == move[3]:
|
if move[0] == 20 and move[2] == 30: # Kings
|
||||||
return {'type': TYPE_6_3_1, 'rank': move[1]}
|
return {'type': TYPE_5_KING_BOMB}
|
||||||
|
# if move[0] == move[1] == move[2] or move[1] == move[2] == move[3]:
|
||||||
|
# return {'type': TYPE_6_3_1, 'rank': move[1]}
|
||||||
else:
|
else:
|
||||||
return {'type': TYPE_15_WRONG}
|
return {'type': TYPE_15_WRONG}
|
||||||
else:
|
else:
|
||||||
|
@ -50,7 +52,9 @@ def get_move_type(move):
|
||||||
return {'type': TYPE_8_SERIAL_SINGLE, 'rank': move[0], 'len': len(move)}
|
return {'type': TYPE_8_SERIAL_SINGLE, 'rank': move[0], 'len': len(move)}
|
||||||
|
|
||||||
if move_size == 5:
|
if move_size == 5:
|
||||||
if len(move_dict) == 2:
|
if len(move_dict) == 1:
|
||||||
|
return {'type': TYPE_4_BOMB5, 'rank': move[0]}
|
||||||
|
elif len(move_dict) == 2:
|
||||||
return {'type': TYPE_7_3_2, 'rank': move[2]}
|
return {'type': TYPE_7_3_2, 'rank': move[2]}
|
||||||
else:
|
else:
|
||||||
return {'type': TYPE_15_WRONG}
|
return {'type': TYPE_15_WRONG}
|
||||||
|
@ -60,13 +64,21 @@ def get_move_type(move):
|
||||||
count_dict[n] += 1
|
count_dict[n] += 1
|
||||||
|
|
||||||
if move_size == 6:
|
if move_size == 6:
|
||||||
if (len(move_dict) == 2 or len(move_dict) == 3) and count_dict.get(4) == 1 and \
|
if len(move_dict) == 1:
|
||||||
(count_dict.get(2) == 1 or count_dict.get(1) == 2):
|
return {'type': TYPE_4_BOMB6, 'rank': move[0]}
|
||||||
return {'type': TYPE_13_4_2, 'rank': move[2]}
|
# if (len(move_dict) == 2 or len(move_dict) == 3) and count_dict.get(4) == 1 and \
|
||||||
|
# (count_dict.get(2) == 1 or count_dict.get(1) == 2):
|
||||||
|
# return {'type': TYPE_13_4_2, 'rank': move[2]}
|
||||||
|
|
||||||
if move_size == 8 and (((len(move_dict) == 3 or len(move_dict) == 2) and
|
# if move_size == 8 and (((len(move_dict) == 3 or len(move_dict) == 2) and
|
||||||
(count_dict.get(4) == 1 and count_dict.get(2) == 2)) or count_dict.get(4) == 2):
|
# (count_dict.get(4) == 1 and count_dict.get(2) == 2)) or count_dict.get(4) == 2):
|
||||||
return {'type': TYPE_14_4_22, 'rank': max([c for c, n in move_dict.items() if n == 4])}
|
# return {'type': TYPE_14_4_22, 'rank': max([c for c, n in move_dict.items() if n == 4])}
|
||||||
|
|
||||||
|
if move_size == 7 and len(move_dict) == 1:
|
||||||
|
return {'type': TYPE_4_BOMB7, 'rank': move[0]}
|
||||||
|
|
||||||
|
if move_size == 8 and len(move_dict) == 1:
|
||||||
|
return {'type': TYPE_4_BOMB8, 'rank': move[0]}
|
||||||
|
|
||||||
mdkeys = sorted(move_dict.keys())
|
mdkeys = sorted(move_dict.keys())
|
||||||
if len(move_dict) == count_dict.get(2) and is_continuous_seq(mdkeys):
|
if len(move_dict) == count_dict.get(2) and is_continuous_seq(mdkeys):
|
||||||
|
@ -93,15 +105,15 @@ def get_move_type(move):
|
||||||
|
|
||||||
serial_3.sort()
|
serial_3.sort()
|
||||||
if is_continuous_seq(serial_3):
|
if is_continuous_seq(serial_3):
|
||||||
if len(serial_3) == len(single)+len(pair)*2:
|
# if len(serial_3) == len(single)+len(pair)*2:
|
||||||
return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3)}
|
# return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3)}
|
||||||
if len(serial_3) == len(pair) and len(move_dict) == len(serial_3) * 2:
|
if len(serial_3) == len(pair) and len(move_dict) == len(serial_3) * 2:
|
||||||
return {'type': TYPE_12_SERIAL_3_2, 'rank': serial_3[0], 'len': len(serial_3)}
|
return {'type': TYPE_12_SERIAL_3_2, 'rank': serial_3[0], 'len': len(serial_3)}
|
||||||
|
|
||||||
if len(serial_3) == 4:
|
# if len(serial_3) == 4:
|
||||||
if is_continuous_seq(serial_3[1:]):
|
# if is_continuous_seq(serial_3[1:]):
|
||||||
return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[1], 'len': len(serial_3) - 1}
|
# return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[1], 'len': len(serial_3) - 1}
|
||||||
if is_continuous_seq(serial_3[:-1]):
|
# if is_continuous_seq(serial_3[:-1]):
|
||||||
return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3) - 1}
|
# return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3) - 1}
|
||||||
|
|
||||||
return {'type': TYPE_15_WRONG}
|
return {'type': TYPE_15_WRONG}
|
||||||
|
|
|
@ -3,7 +3,9 @@ import collections
|
||||||
import itertools
|
import itertools
|
||||||
|
|
||||||
class MovesGener(object):
|
class MovesGener(object):
|
||||||
|
"""
|
||||||
|
This is for generating the possible combinations
|
||||||
|
"""
|
||||||
def __init__(self, cards_list):
|
def __init__(self, cards_list):
|
||||||
self.cards_list = cards_list
|
self.cards_list = cards_list
|
||||||
self.cards_dict = collections.defaultdict(int)
|
self.cards_dict = collections.defaultdict(int)
|
||||||
|
@ -89,17 +91,17 @@ class MovesGener(object):
|
||||||
self.triple_cards_moves.append([k, k, k])
|
self.triple_cards_moves.append([k, k, k])
|
||||||
return self.triple_cards_moves
|
return self.triple_cards_moves
|
||||||
|
|
||||||
def gen_type_4_bomb(self):
|
def gen_type_4_bomb(self, num = 4):
|
||||||
self.bomb_moves = []
|
self.bomb_moves = []
|
||||||
for k, v in self.cards_dict.items():
|
for k, v in self.cards_dict.items():
|
||||||
if v == 4:
|
if v == num:
|
||||||
self.bomb_moves.append([k, k, k, k])
|
self.bomb_moves.append([k] * num)
|
||||||
return self.bomb_moves
|
return self.bomb_moves
|
||||||
|
|
||||||
def gen_type_5_king_bomb(self):
|
def gen_type_5_king_bomb(self):
|
||||||
self.final_bomb_moves = []
|
self.final_bomb_moves = []
|
||||||
if 20 in self.cards_list and 30 in self.cards_list:
|
if 20 in self.cards_list and self.cards_dict[20] == 2 and 30 in self.cards_list and self.cards_dict[30] == 2:
|
||||||
self.final_bomb_moves.append([20, 30])
|
self.final_bomb_moves.append([20, 20, 30, 30])
|
||||||
return self.final_bomb_moves
|
return self.final_bomb_moves
|
||||||
|
|
||||||
def gen_type_6_3_1(self):
|
def gen_type_6_3_1(self):
|
||||||
|
@ -203,15 +205,19 @@ class MovesGener(object):
|
||||||
moves.extend(self.gen_type_1_single())
|
moves.extend(self.gen_type_1_single())
|
||||||
moves.extend(self.gen_type_2_pair())
|
moves.extend(self.gen_type_2_pair())
|
||||||
moves.extend(self.gen_type_3_triple())
|
moves.extend(self.gen_type_3_triple())
|
||||||
moves.extend(self.gen_type_4_bomb())
|
moves.extend(self.gen_type_4_bomb(4))
|
||||||
|
moves.extend(self.gen_type_4_bomb(5))
|
||||||
|
moves.extend(self.gen_type_4_bomb(6))
|
||||||
|
moves.extend(self.gen_type_4_bomb(7))
|
||||||
|
moves.extend(self.gen_type_4_bomb(8))
|
||||||
moves.extend(self.gen_type_5_king_bomb())
|
moves.extend(self.gen_type_5_king_bomb())
|
||||||
moves.extend(self.gen_type_6_3_1())
|
# moves.extend(self.gen_type_6_3_1())
|
||||||
moves.extend(self.gen_type_7_3_2())
|
moves.extend(self.gen_type_7_3_2())
|
||||||
moves.extend(self.gen_type_8_serial_single())
|
moves.extend(self.gen_type_8_serial_single())
|
||||||
moves.extend(self.gen_type_9_serial_pair())
|
moves.extend(self.gen_type_9_serial_pair())
|
||||||
moves.extend(self.gen_type_10_serial_triple())
|
moves.extend(self.gen_type_10_serial_triple())
|
||||||
moves.extend(self.gen_type_11_serial_3_1())
|
# moves.extend(self.gen_type_11_serial_3_1())
|
||||||
moves.extend(self.gen_type_12_serial_3_2())
|
moves.extend(self.gen_type_12_serial_3_2())
|
||||||
moves.extend(self.gen_type_13_4_2())
|
# moves.extend(self.gen_type_13_4_2())
|
||||||
moves.extend(self.gen_type_14_4_22())
|
# moves.extend(self.gen_type_14_4_22())
|
||||||
return moves
|
return moves
|
||||||
|
|
|
@ -9,7 +9,6 @@ def common_handle(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_1_single(moves, rival_move):
|
def filter_type_1_single(moves, rival_move):
|
||||||
return common_handle(moves, rival_move)
|
return common_handle(moves, rival_move)
|
||||||
|
|
||||||
|
@ -25,7 +24,6 @@ def filter_type_3_triple(moves, rival_move):
|
||||||
def filter_type_4_bomb(moves, rival_move):
|
def filter_type_4_bomb(moves, rival_move):
|
||||||
return common_handle(moves, rival_move)
|
return common_handle(moves, rival_move)
|
||||||
|
|
||||||
|
|
||||||
# No need to filter for type_5_king_bomb
|
# No need to filter for type_5_king_bomb
|
||||||
|
|
||||||
def filter_type_6_3_1(moves, rival_move):
|
def filter_type_6_3_1(moves, rival_move):
|
||||||
|
@ -39,7 +37,6 @@ def filter_type_6_3_1(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_7_3_2(moves, rival_move):
|
def filter_type_7_3_2(moves, rival_move):
|
||||||
rival_move.sort()
|
rival_move.sort()
|
||||||
rival_rank = rival_move[2]
|
rival_rank = rival_move[2]
|
||||||
|
@ -51,19 +48,15 @@ def filter_type_7_3_2(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_8_serial_single(moves, rival_move):
|
def filter_type_8_serial_single(moves, rival_move):
|
||||||
return common_handle(moves, rival_move)
|
return common_handle(moves, rival_move)
|
||||||
|
|
||||||
|
|
||||||
def filter_type_9_serial_pair(moves, rival_move):
|
def filter_type_9_serial_pair(moves, rival_move):
|
||||||
return common_handle(moves, rival_move)
|
return common_handle(moves, rival_move)
|
||||||
|
|
||||||
|
|
||||||
def filter_type_10_serial_triple(moves, rival_move):
|
def filter_type_10_serial_triple(moves, rival_move):
|
||||||
return common_handle(moves, rival_move)
|
return common_handle(moves, rival_move)
|
||||||
|
|
||||||
|
|
||||||
def filter_type_11_serial_3_1(moves, rival_move):
|
def filter_type_11_serial_3_1(moves, rival_move):
|
||||||
rival = collections.Counter(rival_move)
|
rival = collections.Counter(rival_move)
|
||||||
rival_rank = max([k for k, v in rival.items() if v == 3])
|
rival_rank = max([k for k, v in rival.items() if v == 3])
|
||||||
|
@ -75,7 +68,6 @@ def filter_type_11_serial_3_1(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_12_serial_3_2(moves, rival_move):
|
def filter_type_12_serial_3_2(moves, rival_move):
|
||||||
rival = collections.Counter(rival_move)
|
rival = collections.Counter(rival_move)
|
||||||
rival_rank = max([k for k, v in rival.items() if v == 3])
|
rival_rank = max([k for k, v in rival.items() if v == 3])
|
||||||
|
@ -87,7 +79,6 @@ def filter_type_12_serial_3_2(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_13_4_2(moves, rival_move):
|
def filter_type_13_4_2(moves, rival_move):
|
||||||
rival_move.sort()
|
rival_move.sort()
|
||||||
rival_rank = rival_move[2]
|
rival_rank = rival_move[2]
|
||||||
|
@ -99,7 +90,6 @@ def filter_type_13_4_2(moves, rival_move):
|
||||||
new_moves.append(move)
|
new_moves.append(move)
|
||||||
return new_moves
|
return new_moves
|
||||||
|
|
||||||
|
|
||||||
def filter_type_14_4_22(moves, rival_move):
|
def filter_type_14_4_22(moves, rival_move):
|
||||||
rival = collections.Counter(rival_move)
|
rival = collections.Counter(rival_move)
|
||||||
rival_rank = my_rank = 0
|
rival_rank = my_rank = 0
|
||||||
|
|
|
@ -10,17 +10,22 @@ TYPE_0_PASS = 0
|
||||||
TYPE_1_SINGLE = 1
|
TYPE_1_SINGLE = 1
|
||||||
TYPE_2_PAIR = 2
|
TYPE_2_PAIR = 2
|
||||||
TYPE_3_TRIPLE = 3
|
TYPE_3_TRIPLE = 3
|
||||||
TYPE_4_BOMB = 4
|
TYPE_4_BOMB = 44
|
||||||
TYPE_5_KING_BOMB = 5
|
TYPE_4_BOMB5 = 45
|
||||||
|
TYPE_4_BOMB6 = 46
|
||||||
|
TYPE_4_BOMB7 = 47
|
||||||
|
TYPE_4_BOMB8 = 48
|
||||||
|
TYPE_5_KING_BOMB = 50
|
||||||
|
#TYPE_6_3_1 = 6
|
||||||
TYPE_6_3_1 = 6
|
TYPE_6_3_1 = 6
|
||||||
TYPE_7_3_2 = 7
|
TYPE_7_3_2 = 7
|
||||||
TYPE_8_SERIAL_SINGLE = 8
|
TYPE_8_SERIAL_SINGLE = 8
|
||||||
TYPE_9_SERIAL_PAIR = 9
|
TYPE_9_SERIAL_PAIR = 9
|
||||||
TYPE_10_SERIAL_TRIPLE = 10
|
TYPE_10_SERIAL_TRIPLE = 10
|
||||||
TYPE_11_SERIAL_3_1 = 11
|
# TYPE_11_SERIAL_3_1 = 11
|
||||||
TYPE_12_SERIAL_3_2 = 12
|
TYPE_12_SERIAL_3_2 = 12
|
||||||
TYPE_13_4_2 = 13
|
# TYPE_13_4_2 = 13
|
||||||
TYPE_14_4_22 = 14
|
# TYPE_14_4_22 = 14
|
||||||
TYPE_15_WRONG = 15
|
TYPE_15_WRONG = 15
|
||||||
|
|
||||||
# betting round action
|
# betting round action
|
||||||
|
|
|
@ -642,7 +642,6 @@
|
||||||
.playingCards.loose ul.hand li:nth-child(11) { left: 14.0em; }
|
.playingCards.loose ul.hand li:nth-child(11) { left: 14.0em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(12) { left: 15.4em; }
|
.playingCards.loose ul.hand li:nth-child(12) { left: 15.4em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(13) { left: 16.8em; }
|
.playingCards.loose ul.hand li:nth-child(13) { left: 16.8em; }
|
||||||
|
|
||||||
.playingCards.loose ul.hand li:nth-child(14) { left: 18.2em; }
|
.playingCards.loose ul.hand li:nth-child(14) { left: 18.2em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(15) { left: 19.6em; }
|
.playingCards.loose ul.hand li:nth-child(15) { left: 19.6em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(16) { left: 21em; }
|
.playingCards.loose ul.hand li:nth-child(16) { left: 21em; }
|
||||||
|
@ -656,6 +655,13 @@
|
||||||
.playingCards.loose ul.hand li:nth-child(24) { left: 32.2em; }
|
.playingCards.loose ul.hand li:nth-child(24) { left: 32.2em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(25) { left: 33.6em; }
|
.playingCards.loose ul.hand li:nth-child(25) { left: 33.6em; }
|
||||||
.playingCards.loose ul.hand li:nth-child(26) { left: 35em; }
|
.playingCards.loose ul.hand li:nth-child(26) { left: 35em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(27) { left: 36.4em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(28) { left: 37.8em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(29) { left: 39.2em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(30) { left: 40.6em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(31) { left: 42em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(32) { left: 43.4em; }
|
||||||
|
.playingCards.loose ul.hand li:nth-child(33) { left: 44.8em; }
|
||||||
|
|
||||||
.playingCards ul.hand li:nth-child(1) { left: 0; }
|
.playingCards ul.hand li:nth-child(1) { left: 0; }
|
||||||
.playingCards ul.hand li:nth-child(2) { left: 1.1em; }
|
.playingCards ul.hand li:nth-child(2) { left: 1.1em; }
|
||||||
|
@ -684,6 +690,13 @@
|
||||||
.playingCards ul.hand li:nth-child(24) { left: 25.3em; }
|
.playingCards ul.hand li:nth-child(24) { left: 25.3em; }
|
||||||
.playingCards ul.hand li:nth-child(25) { left: 26.4em; }
|
.playingCards ul.hand li:nth-child(25) { left: 26.4em; }
|
||||||
.playingCards ul.hand li:nth-child(26) { left: 27.5em; }
|
.playingCards ul.hand li:nth-child(26) { left: 27.5em; }
|
||||||
|
.playingCards ul.hand li:nth-child(27) { left: 28.4em; }
|
||||||
|
.playingCards ul.hand li:nth-child(28) { left: 29.3em; }
|
||||||
|
.playingCards ul.hand li:nth-child(29) { left: 30.2em; }
|
||||||
|
.playingCards ul.hand li:nth-child(30) { left: 31.1em; }
|
||||||
|
.playingCards ul.hand li:nth-child(31) { left: 32.0em; }
|
||||||
|
.playingCards ul.hand li:nth-child(32) { left: 32.9em; }
|
||||||
|
.playingCards ul.hand li:nth-child(33) { left: 33.8em; }
|
||||||
|
|
||||||
/* rotate cards if rotateHand option is on */
|
/* rotate cards if rotateHand option is on */
|
||||||
.playingCards.rotateHand ul.hand li:nth-child(1) {
|
.playingCards.rotateHand ul.hand li:nth-child(1) {
|
||||||
|
@ -798,3 +811,4 @@
|
||||||
.playingCards ul.deck li:nth-child(30) { left: 58px; bottom: 29px; }
|
.playingCards ul.deck li:nth-child(30) { left: 58px; bottom: 29px; }
|
||||||
.playingCards ul.deck li:nth-child(31) { left: 60px; bottom: 30px; }
|
.playingCards ul.deck li:nth-child(31) { left: 60px; bottom: 30px; }
|
||||||
.playingCards ul.deck li:nth-child(32) { left: 62px; bottom: 31px; }
|
.playingCards ul.deck li:nth-child(32) { left: 62px; bottom: 31px; }
|
||||||
|
.playingCards ul.deck li:nth-child(33) { left: 64px; bottom: 32px; }
|
||||||
|
|
|
@ -100,8 +100,8 @@
|
||||||
|
|
||||||
.player-info {
|
.player-info {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
width: 130px;
|
width: 80px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
div {
|
div {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
@ -118,30 +118,32 @@
|
||||||
#left-player {
|
#left-player {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
top: 10px;
|
top: 80px;
|
||||||
|
|
||||||
.player-main-area {
|
.player-main-area {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|
||||||
.player-hand-up {
|
.player-hand-up {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
margin-left: 150px;
|
margin-left: 10px;
|
||||||
|
margin-top: 100px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand-down {
|
.player-hand-down {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
margin-left: 150px;
|
margin-left: 10px;
|
||||||
|
margin-top: 100px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand-placeholder {
|
.player-hand-placeholder {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
margin-left: 130px;
|
margin-left: 130px;
|
||||||
|
@ -150,38 +152,40 @@
|
||||||
|
|
||||||
.played-card-area {
|
.played-card-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
left: 20px;
|
left: 50px;
|
||||||
top: 20px;
|
top: 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#right-player {
|
#right-player {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 10px;
|
top: 80px;
|
||||||
|
|
||||||
.player-main-area {
|
.player-main-area {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
|
|
||||||
.player-hand-up {
|
.player-hand-up {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
margin-right: 150px;
|
margin-top: 100px;
|
||||||
|
margin-right: 10px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand-down {
|
.player-hand-down {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50px;
|
top: 50px;
|
||||||
|
margin-top: 100px;
|
||||||
margin-right: 150px;
|
margin-right: 150px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand-placeholder {
|
.player-hand-placeholder {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
margin-right: 130px;
|
margin-right: 130px;
|
||||||
|
@ -192,10 +196,53 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.played-card-area {
|
||||||
|
position: relative;
|
||||||
|
right: 50px;
|
||||||
|
top: 80px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#top-player {
|
||||||
|
position: absolute;
|
||||||
|
left: 80px;
|
||||||
|
top: 10px;
|
||||||
|
|
||||||
|
.player-main-area {
|
||||||
|
width: 300px;
|
||||||
|
height: 80px;
|
||||||
|
font-size: 10px;
|
||||||
|
|
||||||
|
.player-hand-up {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 100px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-hand-down {
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-left: 100px;
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player-hand-placeholder {
|
||||||
|
width: 150px;
|
||||||
|
height: 80px;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
margin-right: 130px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.played-card-area {
|
.played-card-area {
|
||||||
position: relative;
|
position: relative;
|
||||||
right: 20px;
|
right: 20px;
|
||||||
top: 20px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,29 +250,29 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
margin-left: -290px;
|
margin-left: -340px;
|
||||||
|
|
||||||
.player-main-area {
|
.player-main-area {
|
||||||
width: 580px;
|
width: 580px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.player-hand {
|
.player-hand {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
margin-left: 130px;
|
margin-left: 80px;
|
||||||
width: 450px;
|
width: 450px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.player-hand-placeholder {
|
.player-hand-placeholder {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 130px;
|
height: 80px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
margin-left: 130px;
|
margin-left: 80px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -163,9 +163,9 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
computeSideHand(cards) {
|
computeSideHand(cards) {
|
||||||
let upCards;
|
let upCards;
|
||||||
let downCards = [];
|
let downCards = [];
|
||||||
if (cards.length > 10) {
|
if (cards.length > 16) {
|
||||||
upCards = cards.slice(0, 10);
|
upCards = cards.slice(0, 16);
|
||||||
downCards = cards.slice(10);
|
downCards = cards.slice(16);
|
||||||
} else {
|
} else {
|
||||||
upCards = cards;
|
upCards = cards;
|
||||||
}
|
}
|
||||||
|
@ -320,11 +320,13 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
return element.id === bottomId;
|
return element.id === bottomId;
|
||||||
});
|
});
|
||||||
const bottomIdx = found ? found.index : -1;
|
const bottomIdx = found ? found.index : -1;
|
||||||
const rightIdx = bottomIdx >= 0 ? (bottomIdx + 1) % 3 : -1;
|
const rightIdx = bottomIdx >= 0 ? (bottomIdx + 1) % 4 : -1;
|
||||||
const leftIdx = rightIdx >= 0 ? (rightIdx + 1) % 3 : -1;
|
const topIdx = rightIdx >= 0 ? (rightIdx + 1) % 4 : -1;
|
||||||
|
const leftIdx = topIdx >= 0 ? (topIdx + 1) % 4 : -1;
|
||||||
let rightId = -1;
|
let rightId = -1;
|
||||||
let leftId = -1;
|
let leftId = -1;
|
||||||
if (rightIdx >= 0 && leftIdx >= 0) {
|
let topId = -1;
|
||||||
|
if (rightIdx >= 0 && leftIdx >= 0 && topIdx >= 0) {
|
||||||
found = this.props.playerInfo.find((element) => {
|
found = this.props.playerInfo.find((element) => {
|
||||||
return element.index === rightIdx;
|
return element.index === rightIdx;
|
||||||
});
|
});
|
||||||
|
@ -333,6 +335,10 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
return element.index === leftIdx;
|
return element.index === leftIdx;
|
||||||
});
|
});
|
||||||
if (found) leftId = found.id;
|
if (found) leftId = found.id;
|
||||||
|
found = this.props.playerInfo.find((element) => {
|
||||||
|
return element.index === topIdx;
|
||||||
|
});
|
||||||
|
if (found) topId = found.id;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -381,6 +387,21 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="played-card-area">{rightIdx >= 0 ? this.playerDecisionArea(rightIdx) : ''}</div>
|
<div className="played-card-area">{rightIdx >= 0 ? this.playerDecisionArea(rightIdx) : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id={'top-player'}>
|
||||||
|
<div className="player-main-area">
|
||||||
|
<div className="player-info">{this.computePlayerPortrait(topId, topIdx)}</div>
|
||||||
|
{topIdx >= 0 ? (
|
||||||
|
<div className="player-hand">
|
||||||
|
{this.computeSideHand(this.props.hands[topIdx])}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="player-hand-placeholder">
|
||||||
|
<span>{t('waiting...')}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="played-card-area">{topIdx >= 0 ? this.playerDecisionArea(topIdx) : ''}</div>
|
||||||
|
</div>
|
||||||
<div id={'bottom-player'}>
|
<div id={'bottom-player'}>
|
||||||
<div className="played-card-area">
|
<div className="played-card-area">
|
||||||
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}
|
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}
|
||||||
|
@ -438,7 +459,12 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => this.props.handleSelectRole('landlord_down')}
|
onClick={() => this.props.handleSelectRole('landlord_down')}
|
||||||
style={{ width: '225px', justifyContent: 'space-evenly' }}
|
style={{
|
||||||
|
width: '225px',
|
||||||
|
justifyContent: 'space-evenly',
|
||||||
|
marginTop: '20px',
|
||||||
|
marginBottom: '20px',
|
||||||
|
}}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" height="48px" />}
|
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" height="48px" />}
|
||||||
|
@ -446,6 +472,16 @@ class DoudizhuGameBoard extends React.Component {
|
||||||
{t('doudizhu.play_as_peasant')}
|
{t('doudizhu.play_as_peasant')}
|
||||||
<br />({t('doudizhu.landlord_down')})
|
<br />({t('doudizhu.landlord_down')})
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => this.props.handleSelectRole('landlord_front')}
|
||||||
|
style={{ width: '225px', justifyContent: 'space-evenly' }}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
startIcon={<img src={Peasant_wName} alt="Peasant" width="48px" height="48px" />}
|
||||||
|
>
|
||||||
|
{t('doudizhu.play_as_peasant')}
|
||||||
|
<br />({t('doudizhu.landlord_front')})
|
||||||
|
</Button>
|
||||||
</Layout.Row>
|
</Layout.Row>
|
||||||
)}
|
)}
|
||||||
{this.props.gamePlayable && this.props.gameStatus === 'localeSelection' && (
|
{this.props.gamePlayable && this.props.gameStatus === 'localeSelection' && (
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"play_as_landlord": "扮演地主",
|
"play_as_landlord": "扮演地主",
|
||||||
"play_as_peasant": "扮演农民",
|
"play_as_peasant": "扮演农民",
|
||||||
"landlord_up": "地主上家",
|
"landlord_up": "地主上家",
|
||||||
|
"landlord_front": "地主对家",
|
||||||
"landlord_down": "地主下家",
|
"landlord_down": "地主下家",
|
||||||
"peasants_win": "农民胜利!",
|
"peasants_win": "农民胜利!",
|
||||||
"landlord_win": "地主胜利!",
|
"landlord_win": "地主胜利!",
|
||||||
|
|
|
@ -51,12 +51,12 @@ export function translateCardData(card) {
|
||||||
let rankText;
|
let rankText;
|
||||||
let suitText = '';
|
let suitText = '';
|
||||||
// translate rank
|
// translate rank
|
||||||
if (card === 'RJ') {
|
if (card === 'RJ' || card === 'RJ2') {
|
||||||
rankClass = 'big';
|
rankClass = 'big';
|
||||||
rankText = '+';
|
rankText = '+';
|
||||||
suitClass = 'joker';
|
suitClass = 'joker';
|
||||||
suitText = 'Joker';
|
suitText = 'Joker';
|
||||||
} else if (card === 'BJ') {
|
} else if (card === 'BJ' || card === 'BJ2') {
|
||||||
rankClass = 'little';
|
rankClass = 'little';
|
||||||
rankText = '-';
|
rankText = '-';
|
||||||
suitClass = 'joker';
|
suitClass = 'joker';
|
||||||
|
@ -67,7 +67,7 @@ export function translateCardData(card) {
|
||||||
rankText = card.charAt(1) === 'T' ? `10` : card.charAt(1);
|
rankText = card.charAt(1) === 'T' ? `10` : card.charAt(1);
|
||||||
}
|
}
|
||||||
// translate suitClass
|
// translate suitClass
|
||||||
if (card !== 'RJ' && card !== 'BJ') {
|
if (card !== 'RJ' && card !== 'BJ' && card !== 'RJ2' && card !== 'BJ2') {
|
||||||
suitClass = suitMap.get(card.charAt(0));
|
suitClass = suitMap.get(card.charAt(0));
|
||||||
suitText = suitMapSymbol.get(card.charAt(0));
|
suitText = suitMapSymbol.get(card.charAt(0));
|
||||||
}
|
}
|
||||||
|
@ -101,9 +101,9 @@ export function computeHandCardsWidth(num, emWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function card2SuiteAndRank(card) {
|
export function card2SuiteAndRank(card) {
|
||||||
if (card === 'BJ' || card === 'B') {
|
if (card === 'BJ' || card === 'B' || card === 'BJ2' || card === 'B2') {
|
||||||
return { suite: null, rank: 'X' };
|
return { suite: null, rank: 'X' };
|
||||||
} else if (card === 'RJ' || card === 'R') {
|
} else if (card === 'RJ' || card === 'R' || card === 'RJ2' || card === 'R') {
|
||||||
return { suite: null, rank: 'D' };
|
return { suite: null, rank: 'D' };
|
||||||
} else {
|
} else {
|
||||||
return { suite: card[0], rank: card[1] };
|
return { suite: card[0], rank: card[1] };
|
||||||
|
@ -165,6 +165,60 @@ export const fullDoudizhuDeck = [
|
||||||
'C3',
|
'C3',
|
||||||
'H3',
|
'H3',
|
||||||
'D3',
|
'D3',
|
||||||
|
'RJ2',
|
||||||
|
'BJ2',
|
||||||
|
'S22',
|
||||||
|
'C22',
|
||||||
|
'H22',
|
||||||
|
'D22',
|
||||||
|
'SA2',
|
||||||
|
'CA2',
|
||||||
|
'HA2',
|
||||||
|
'DA2',
|
||||||
|
'SK2',
|
||||||
|
'CK2',
|
||||||
|
'HK2',
|
||||||
|
'DK2',
|
||||||
|
'SQ2',
|
||||||
|
'CQ2',
|
||||||
|
'HQ2',
|
||||||
|
'DQ2',
|
||||||
|
'SJ2',
|
||||||
|
'CJ2',
|
||||||
|
'HJ2',
|
||||||
|
'DJ2',
|
||||||
|
'ST2',
|
||||||
|
'CT2',
|
||||||
|
'HT2',
|
||||||
|
'DT2',
|
||||||
|
'S92',
|
||||||
|
'C92',
|
||||||
|
'H92',
|
||||||
|
'D92',
|
||||||
|
'S82',
|
||||||
|
'C82',
|
||||||
|
'H82',
|
||||||
|
'D82',
|
||||||
|
'S72',
|
||||||
|
'C72',
|
||||||
|
'H72',
|
||||||
|
'D72',
|
||||||
|
'S62',
|
||||||
|
'C62',
|
||||||
|
'H62',
|
||||||
|
'D62',
|
||||||
|
'S52',
|
||||||
|
'C52',
|
||||||
|
'H52',
|
||||||
|
'D52',
|
||||||
|
'S42',
|
||||||
|
'C42',
|
||||||
|
'H42',
|
||||||
|
'D42',
|
||||||
|
'S32',
|
||||||
|
'C32',
|
||||||
|
'H32',
|
||||||
|
'D32',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const fullDoudizhuDeckIndex = {
|
export const fullDoudizhuDeckIndex = {
|
||||||
|
@ -222,6 +276,60 @@ export const fullDoudizhuDeckIndex = {
|
||||||
C3: 3,
|
C3: 3,
|
||||||
H3: 2,
|
H3: 2,
|
||||||
D3: 1,
|
D3: 1,
|
||||||
|
RJ2: 54,
|
||||||
|
BJ2: 53,
|
||||||
|
S22: 52,
|
||||||
|
C22: 51,
|
||||||
|
H22: 50,
|
||||||
|
D22: 49,
|
||||||
|
SA2: 48,
|
||||||
|
CA2: 47,
|
||||||
|
HA2: 46,
|
||||||
|
DA2: 45,
|
||||||
|
SK2: 44,
|
||||||
|
CK2: 43,
|
||||||
|
HK2: 42,
|
||||||
|
DK2: 41,
|
||||||
|
SQ2: 40,
|
||||||
|
CQ2: 39,
|
||||||
|
HQ2: 38,
|
||||||
|
DQ2: 37,
|
||||||
|
SJ2: 36,
|
||||||
|
CJ2: 35,
|
||||||
|
HJ2: 34,
|
||||||
|
DJ2: 33,
|
||||||
|
ST2: 32,
|
||||||
|
CT2: 31,
|
||||||
|
HT2: 30,
|
||||||
|
DT2: 29,
|
||||||
|
S92: 28,
|
||||||
|
C92: 27,
|
||||||
|
H92: 26,
|
||||||
|
D92: 25,
|
||||||
|
S82: 24,
|
||||||
|
C82: 23,
|
||||||
|
H82: 22,
|
||||||
|
D82: 21,
|
||||||
|
S72: 20,
|
||||||
|
C72: 19,
|
||||||
|
H72: 18,
|
||||||
|
D72: 17,
|
||||||
|
S62: 16,
|
||||||
|
C62: 15,
|
||||||
|
H62: 14,
|
||||||
|
D62: 13,
|
||||||
|
S52: 12,
|
||||||
|
C52: 11,
|
||||||
|
H52: 10,
|
||||||
|
D52: 9,
|
||||||
|
S42: 8,
|
||||||
|
C42: 7,
|
||||||
|
H42: 6,
|
||||||
|
D42: 5,
|
||||||
|
S32: 4,
|
||||||
|
C32: 3,
|
||||||
|
H32: 2,
|
||||||
|
D32: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function sortDoudizhuCards(cards, ascending = false) {
|
export function sortDoudizhuCards(cards, ascending = false) {
|
||||||
|
@ -234,10 +342,38 @@ export function sortDoudizhuCards(cards, ascending = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDoudizhuBomb(cards) {
|
export function isDoudizhuBomb(cards) {
|
||||||
if (cards.length === 2) return (cards[0] === 'RJ' && cards[1] === 'BJ') || (cards[0] === 'BJ' && cards[1] === 'RJ');
|
if(cards.length <= 3 || cards.length >= 9)
|
||||||
if (cards.length === 4)
|
return -1;
|
||||||
return cards[0][1] === cards[1][1] && cards[0][1] === cards[2][1] && cards[0][1] === cards[3][1];
|
const distinctedCards = [...new Set(cards.map(x=>x.substring(0, 2)))];
|
||||||
return false;
|
switch(cards.length) {
|
||||||
|
case 4:
|
||||||
|
if(distinctedCards.length == 2) {
|
||||||
|
if((distinctedCards[0] === 'RJ' && distinctedCards[1] === 'BJ') || (distinctedCards[0] === 'BJ' && distinctedCards[1] === 'RJ')) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(distinctedCards.length == 1) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
case 5:
|
||||||
|
if(distinctedCards.length == 1) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
if(distinctedCards.length == 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
case 8:
|
||||||
|
if(distinctedCards.length == 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shuffleArray(inputArray) {
|
export function shuffleArray(inputArray) {
|
||||||
|
|
|
@ -33,7 +33,7 @@ import { douzeroDemoUrl } from '../../utils/config';
|
||||||
|
|
||||||
let shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
let shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
||||||
|
|
||||||
let threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
|
let threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 8)));
|
||||||
let originalThreeLandlordCards = threeLandlordCards.slice();
|
let originalThreeLandlordCards = threeLandlordCards.slice();
|
||||||
|
|
||||||
const initConsiderationTime = 30000;
|
const initConsiderationTime = 30000;
|
||||||
|
@ -42,9 +42,10 @@ const mainPlayerId = 0; // index of main player (for the sake of simplify code l
|
||||||
let playerInfo = [];
|
let playerInfo = [];
|
||||||
|
|
||||||
let initHands = [
|
let initHands = [
|
||||||
shuffledDoudizhuDeck.slice(3, 20),
|
shuffledDoudizhuDeck.slice(8, 33),
|
||||||
shuffledDoudizhuDeck.slice(20, 37),
|
shuffledDoudizhuDeck.slice(33, 58),
|
||||||
shuffledDoudizhuDeck.slice(37, 54),
|
shuffledDoudizhuDeck.slice(58, 83),
|
||||||
|
shuffledDoudizhuDeck.slice(83, 108),
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log('init hands', initHands);
|
console.log('init hands', initHands);
|
||||||
|
@ -53,12 +54,14 @@ console.log('three landlord card', threeLandlordCards);
|
||||||
let gameStateTimeout = null;
|
let gameStateTimeout = null;
|
||||||
|
|
||||||
let gameHistory = [];
|
let gameHistory = [];
|
||||||
let bombNum = 0;
|
let bombNum = [0, 0 , 0];
|
||||||
let lastMoveLandlord = [];
|
let lastMoveLandlord = [];
|
||||||
let lastMoveLandlordDown = [];
|
let lastMoveLandlordDown = [];
|
||||||
|
let lastMoveLandlordFront = [];
|
||||||
let lastMoveLandlordUp = [];
|
let lastMoveLandlordUp = [];
|
||||||
let playedCardsLandlord = [];
|
let playedCardsLandlord = [];
|
||||||
let playedCardsLandlordDown = [];
|
let playedCardsLandlordDown = [];
|
||||||
|
let playedCardsLandlordFront = [];
|
||||||
let playedCardsLandlordUp = [];
|
let playedCardsLandlordUp = [];
|
||||||
let legalActions = { turn: -1, actions: [] };
|
let legalActions = { turn: -1, actions: [] };
|
||||||
let hintIdx = -1;
|
let hintIdx = -1;
|
||||||
|
@ -73,8 +76,8 @@ function PvEDoudizhuDemoView() {
|
||||||
const [toggleFade, setToggleFade] = useState('');
|
const [toggleFade, setToggleFade] = useState('');
|
||||||
const [gameStatus, setGameStatus] = useState(localStorage.getItem('LOCALE') ? 'ready' : 'localeSelection'); // "localeSelection", "ready", "playing", "paused", "over"
|
const [gameStatus, setGameStatus] = useState(localStorage.getItem('LOCALE') ? 'ready' : 'localeSelection'); // "localeSelection", "ready", "playing", "paused", "over"
|
||||||
const [gameState, setGameState] = useState({
|
const [gameState, setGameState] = useState({
|
||||||
hands: [[], [], []],
|
hands: [[], [], [], []],
|
||||||
latestAction: [[], [], []],
|
latestAction: [[], [], [], []],
|
||||||
currentPlayer: null, // index of current player
|
currentPlayer: null, // index of current player
|
||||||
turn: 0,
|
turn: 0,
|
||||||
});
|
});
|
||||||
|
@ -90,8 +93,8 @@ function PvEDoudizhuDemoView() {
|
||||||
const cardArr2DouzeroFormat = (cards) => {
|
const cardArr2DouzeroFormat = (cards) => {
|
||||||
return cards
|
return cards
|
||||||
.map((card) => {
|
.map((card) => {
|
||||||
if (card === 'RJ') return 'D';
|
if (card === 'RJ' || card === 'RJ2') return 'D';
|
||||||
if (card === 'BJ') return 'X';
|
if (card === 'BJ' || card === 'BJ2') return 'X';
|
||||||
return card[1];
|
return card[1];
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
@ -122,12 +125,15 @@ function PvEDoudizhuDemoView() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if next player is user, get legal actions
|
// if next player is user, get legal actions
|
||||||
if ((gameState.currentPlayer + 1) % 3 === mainPlayerId) {
|
if ((gameState.currentPlayer + 1) % 4 === mainPlayerId) {
|
||||||
hintIdx = -1;
|
hintIdx = -1;
|
||||||
const player_hand_cards = cardArr2DouzeroFormat(gameState.hands[mainPlayerId].slice().reverse());
|
const player_hand_cards = cardArr2DouzeroFormat(gameState.hands[mainPlayerId].slice().reverse());
|
||||||
let rival_move = '';
|
let rival_move = '';
|
||||||
if (playingCard.length === 0) {
|
if (playingCard.length === 0) {
|
||||||
rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 1], true));
|
rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 1], true));
|
||||||
|
if(rival_move === '') {
|
||||||
|
rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 2], true));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rival_move = rankOnly ? playingCard.join('') : cardArr2DouzeroFormat(playingCard);
|
rival_move = rankOnly ? playingCard.join('') : cardArr2DouzeroFormat(playingCard);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +148,7 @@ function PvEDoudizhuDemoView() {
|
||||||
actions: data.legal_action.split(','),
|
actions: data.legal_action.split(','),
|
||||||
};
|
};
|
||||||
setIsHintDisabled(data.legal_action === '');
|
setIsHintDisabled(data.legal_action === '');
|
||||||
setIsPassDisabled(playingCard.length === 0 && gameHistory[gameHistory.length - 1].length === 0);
|
setIsPassDisabled(playingCard.length === 0 && gameHistory[gameHistory.length - 1].length === 0 && gameHistory[gameHistory.length - 2].length === 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delay play for api player
|
// delay play for api player
|
||||||
|
@ -200,6 +206,10 @@ function PvEDoudizhuDemoView() {
|
||||||
playedCardsLandlordDown = playedCardsLandlordDown.concat(newHistoryRecord);
|
playedCardsLandlordDown = playedCardsLandlordDown.concat(newHistoryRecord);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
lastMoveLandlordFront = newHistoryRecord;
|
||||||
|
playedCardsLandlordFront = playedCardsLandlordFront.concat(newHistoryRecord);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
lastMoveLandlordUp = newHistoryRecord;
|
lastMoveLandlordUp = newHistoryRecord;
|
||||||
playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
|
playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
|
||||||
break;
|
break;
|
||||||
|
@ -207,11 +217,12 @@ function PvEDoudizhuDemoView() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gameHistory.push(newHistoryRecord);
|
gameHistory.push(newHistoryRecord);
|
||||||
if (isDoudizhuBomb(newHistoryRecord)) bombNum++;
|
const bombLevel = isDoudizhuBomb(newHistoryRecord);
|
||||||
|
if (bombLevel >= 0) bombNum[bombLevel]++;
|
||||||
|
|
||||||
newGameState.latestAction[gameState.currentPlayer] = newLatestAction;
|
newGameState.latestAction[gameState.currentPlayer] = newLatestAction;
|
||||||
newGameState.hands[gameState.currentPlayer] = newHand;
|
newGameState.hands[gameState.currentPlayer] = newHand;
|
||||||
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3;
|
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 4;
|
||||||
newGameState.turn++;
|
newGameState.turn++;
|
||||||
if (newHand.length === 0) {
|
if (newHand.length === 0) {
|
||||||
setGameStatus('over');
|
setGameStatus('over');
|
||||||
|
@ -241,6 +252,8 @@ function PvEDoudizhuDemoView() {
|
||||||
landlordWinNum: 0,
|
landlordWinNum: 0,
|
||||||
landlordUpGameNum: 0,
|
landlordUpGameNum: 0,
|
||||||
landlordUpWinNum: 0,
|
landlordUpWinNum: 0,
|
||||||
|
landlordFrontGameNum: 0,
|
||||||
|
landlordFrontWinNum: 0,
|
||||||
landlordDownGameNum: 0,
|
landlordDownGameNum: 0,
|
||||||
landlordDownWinNum: 0,
|
landlordDownWinNum: 0,
|
||||||
};
|
};
|
||||||
|
@ -262,6 +275,13 @@ function PvEDoudizhuDemoView() {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
gameStatistics.landlordFrontGameNum += 1;
|
||||||
|
if (winner.role === playerInfo[mainPlayerId].role) {
|
||||||
|
gameStatistics.totalWinNum += 1;
|
||||||
|
gameStatistics.landlordDownWinNum += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
gameStatistics.landlordUpGameNum += 1;
|
gameStatistics.landlordUpGameNum += 1;
|
||||||
if (winner.role === playerInfo[mainPlayerId].role) {
|
if (winner.role === playerInfo[mainPlayerId].role) {
|
||||||
gameStatistics.totalWinNum += 1;
|
gameStatistics.totalWinNum += 1;
|
||||||
|
@ -298,6 +318,15 @@ function PvEDoudizhuDemoView() {
|
||||||
'%'
|
'%'
|
||||||
: '-',
|
: '-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
role: t('doudizhu.landlord_front'),
|
||||||
|
win: gameStatistics.landlordFrontWinNum,
|
||||||
|
total: gameStatistics.landlordFrontGameNum,
|
||||||
|
winRate: gameStatistics.landlordFrontGameNum
|
||||||
|
? ((gameStatistics.landlordFrontWinNum / gameStatistics.landlordFrontGameNum) * 100).toFixed(2) +
|
||||||
|
'%'
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
role: t('doudizhu.landlord_down'),
|
role: t('doudizhu.landlord_down'),
|
||||||
win: gameStatistics.landlordDownWinNum,
|
win: gameStatistics.landlordDownWinNum,
|
||||||
|
@ -335,9 +364,11 @@ function PvEDoudizhuDemoView() {
|
||||||
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 0).index].length;
|
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 0).index].length;
|
||||||
const num_cards_left_landlord_down =
|
const num_cards_left_landlord_down =
|
||||||
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 1).index].length;
|
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 1).index].length;
|
||||||
const num_cards_left_landlord_up =
|
const num_cards_left_landlord_front =
|
||||||
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 2).index].length;
|
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 2).index].length;
|
||||||
const three_landlord_cards = cardArr2DouzeroFormat(threeLandlordCards.slice().reverse());
|
const num_cards_left_landlord_up =
|
||||||
|
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 3).index].length;
|
||||||
|
// const three_landlord_cards = cardArr2DouzeroFormat(threeLandlordCards.slice().reverse());
|
||||||
const card_play_action_seq = gameHistory
|
const card_play_action_seq = gameHistory
|
||||||
.map((cards) => {
|
.map((cards) => {
|
||||||
return cardArr2DouzeroFormat(cards);
|
return cardArr2DouzeroFormat(cards);
|
||||||
|
@ -345,18 +376,21 @@ function PvEDoudizhuDemoView() {
|
||||||
.join(',');
|
.join(',');
|
||||||
const other_hand_cards = cardArr2DouzeroFormat(
|
const other_hand_cards = cardArr2DouzeroFormat(
|
||||||
sortDoudizhuCards(
|
sortDoudizhuCards(
|
||||||
gameState.hands[(gameState.currentPlayer + 1) % 3].concat(
|
gameState.hands[(gameState.currentPlayer + 1) % 4].concat(
|
||||||
gameState.hands[(gameState.currentPlayer + 2) % 3],
|
gameState.hands[(gameState.currentPlayer + 2) % 4],
|
||||||
|
gameState.hands[(gameState.currentPlayer + 3) % 4],
|
||||||
),
|
),
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
const last_move_landlord = cardArr2DouzeroFormat(lastMoveLandlord.slice().reverse());
|
const last_move_landlord = cardArr2DouzeroFormat(lastMoveLandlord.slice().reverse());
|
||||||
const last_move_landlord_down = cardArr2DouzeroFormat(lastMoveLandlordDown.slice().reverse());
|
const last_move_landlord_down = cardArr2DouzeroFormat(lastMoveLandlordDown.slice().reverse());
|
||||||
|
const last_move_landlord_front = cardArr2DouzeroFormat(lastMoveLandlordFront.slice().reverse());
|
||||||
const last_move_landlord_up = cardArr2DouzeroFormat(lastMoveLandlordUp.slice().reverse());
|
const last_move_landlord_up = cardArr2DouzeroFormat(lastMoveLandlordUp.slice().reverse());
|
||||||
const bomb_num = bombNum;
|
const bomb_num = bombNum.join(',');
|
||||||
const played_cards_landlord = cardArr2DouzeroFormat(playedCardsLandlord);
|
const played_cards_landlord = cardArr2DouzeroFormat(playedCardsLandlord);
|
||||||
const played_cards_landlord_down = cardArr2DouzeroFormat(playedCardsLandlordDown);
|
const played_cards_landlord_down = cardArr2DouzeroFormat(playedCardsLandlordDown);
|
||||||
|
const played_cards_landlord_front = cardArr2DouzeroFormat(playedCardsLandlordFront);
|
||||||
const played_cards_landlord_up = cardArr2DouzeroFormat(playedCardsLandlordUp);
|
const played_cards_landlord_up = cardArr2DouzeroFormat(playedCardsLandlordUp);
|
||||||
|
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
|
@ -364,16 +398,19 @@ function PvEDoudizhuDemoView() {
|
||||||
player_hand_cards,
|
player_hand_cards,
|
||||||
num_cards_left_landlord,
|
num_cards_left_landlord,
|
||||||
num_cards_left_landlord_down,
|
num_cards_left_landlord_down,
|
||||||
|
num_cards_left_landlord_front,
|
||||||
num_cards_left_landlord_up,
|
num_cards_left_landlord_up,
|
||||||
three_landlord_cards,
|
// three_landlord_cards,
|
||||||
card_play_action_seq,
|
card_play_action_seq,
|
||||||
other_hand_cards,
|
other_hand_cards,
|
||||||
last_move_landlord,
|
last_move_landlord,
|
||||||
last_move_landlord_down,
|
last_move_landlord_down,
|
||||||
|
last_move_landlord_front,
|
||||||
last_move_landlord_up,
|
last_move_landlord_up,
|
||||||
bomb_num,
|
bomb_num,
|
||||||
played_cards_landlord,
|
played_cards_landlord,
|
||||||
played_cards_landlord_down,
|
played_cards_landlord_down,
|
||||||
|
played_cards_landlord_front,
|
||||||
played_cards_landlord_up,
|
played_cards_landlord_up,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -396,6 +433,10 @@ function PvEDoudizhuDemoView() {
|
||||||
rival_move = cardArr2DouzeroFormat(
|
rival_move = cardArr2DouzeroFormat(
|
||||||
sortDoudizhuCards(gameHistory[gameHistory.length - 2], true),
|
sortDoudizhuCards(gameHistory[gameHistory.length - 2], true),
|
||||||
);
|
);
|
||||||
|
} else if (gameHistory.length >= 3 && gameHistory[gameHistory.length - 3].length > 0) {
|
||||||
|
rival_move = cardArr2DouzeroFormat(
|
||||||
|
sortDoudizhuCards(gameHistory[gameHistory.length - 3], true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const requestBody = {
|
const requestBody = {
|
||||||
player_hand_cards,
|
player_hand_cards,
|
||||||
|
@ -500,6 +541,12 @@ function PvEDoudizhuDemoView() {
|
||||||
role: 'peasant',
|
role: 'peasant',
|
||||||
douzeroPlayerPosition: -1,
|
douzeroPlayerPosition: -1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
index: 3,
|
||||||
|
role: 'peasant',
|
||||||
|
douzeroPlayerPosition: -1,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
switch (role) {
|
switch (role) {
|
||||||
case 'landlord_up':
|
case 'landlord_up':
|
||||||
|
@ -511,6 +558,10 @@ function PvEDoudizhuDemoView() {
|
||||||
playerInfo[0].role = 'landlord';
|
playerInfo[0].role = 'landlord';
|
||||||
break;
|
break;
|
||||||
case 'landlord_down':
|
case 'landlord_down':
|
||||||
|
playerInfo = deepCopy(playerInfoTemplate);
|
||||||
|
playerInfo[3].role = 'landlord';
|
||||||
|
break;
|
||||||
|
case 'landlord_front':
|
||||||
playerInfo = deepCopy(playerInfoTemplate);
|
playerInfo = deepCopy(playerInfoTemplate);
|
||||||
playerInfo[2].role = 'landlord';
|
playerInfo[2].role = 'landlord';
|
||||||
break;
|
break;
|
||||||
|
@ -519,8 +570,9 @@ function PvEDoudizhuDemoView() {
|
||||||
}
|
}
|
||||||
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
|
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
|
||||||
playerInfo[landlordIdx].douzeroPlayerPosition = 0;
|
playerInfo[landlordIdx].douzeroPlayerPosition = 0;
|
||||||
playerInfo[(landlordIdx + 1) % 3].douzeroPlayerPosition = 1;
|
playerInfo[(landlordIdx + 1) % 4].douzeroPlayerPosition = 1;
|
||||||
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
|
playerInfo[(landlordIdx + 2) % 4].douzeroPlayerPosition = 2;
|
||||||
|
playerInfo[(landlordIdx + 3) % 4].douzeroPlayerPosition = 3;
|
||||||
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
||||||
setGameStatus('playing');
|
setGameStatus('playing');
|
||||||
syncGameStatus = 'playing';
|
syncGameStatus = 'playing';
|
||||||
|
@ -556,6 +608,12 @@ function PvEDoudizhuDemoView() {
|
||||||
total: 0,
|
total: 0,
|
||||||
winRate: '-',
|
winRate: '-',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
role: t('doudizhu.landlord_front'),
|
||||||
|
win: 0,
|
||||||
|
total: 0,
|
||||||
|
winRate: '-',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
role: t('doudizhu.landlord_down'),
|
role: t('doudizhu.landlord_down'),
|
||||||
win: 0,
|
win: 0,
|
||||||
|
@ -575,25 +633,28 @@ function PvEDoudizhuDemoView() {
|
||||||
// reset all game state for new game
|
// reset all game state for new game
|
||||||
shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
||||||
|
|
||||||
threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
|
threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 8)));
|
||||||
originalThreeLandlordCards = threeLandlordCards.slice();
|
originalThreeLandlordCards = threeLandlordCards.slice();
|
||||||
|
|
||||||
initHands = [
|
initHands = [
|
||||||
shuffledDoudizhuDeck.slice(3, 20),
|
shuffledDoudizhuDeck.slice(8, 33),
|
||||||
shuffledDoudizhuDeck.slice(20, 37),
|
shuffledDoudizhuDeck.slice(33, 58),
|
||||||
shuffledDoudizhuDeck.slice(37, 54),
|
shuffledDoudizhuDeck.slice(58, 83),
|
||||||
|
shuffledDoudizhuDeck.slice(83, 108),
|
||||||
];
|
];
|
||||||
|
|
||||||
playerInfo = [];
|
playerInfo = [];
|
||||||
|
|
||||||
gameStateTimeout = null;
|
gameStateTimeout = null;
|
||||||
gameHistory = [];
|
gameHistory = [];
|
||||||
bombNum = 0;
|
bombNum = [0, 0, 0];
|
||||||
lastMoveLandlord = [];
|
lastMoveLandlord = [];
|
||||||
lastMoveLandlordDown = [];
|
lastMoveLandlordDown = [];
|
||||||
|
lastMoveLandlordFront = [];
|
||||||
lastMoveLandlordUp = [];
|
lastMoveLandlordUp = [];
|
||||||
playedCardsLandlord = [];
|
playedCardsLandlord = [];
|
||||||
playedCardsLandlordDown = [];
|
playedCardsLandlordDown = [];
|
||||||
|
playedCardsLandlordFront = [];
|
||||||
playedCardsLandlordUp = [];
|
playedCardsLandlordUp = [];
|
||||||
legalActions = { turn: -1, actions: [] };
|
legalActions = { turn: -1, actions: [] };
|
||||||
|
|
||||||
|
@ -602,8 +663,8 @@ function PvEDoudizhuDemoView() {
|
||||||
setIsPassDisabled(true);
|
setIsPassDisabled(true);
|
||||||
setIsHintDisabled(true);
|
setIsHintDisabled(true);
|
||||||
setGameState({
|
setGameState({
|
||||||
hands: [[], [], []],
|
hands: [[], [], [], []],
|
||||||
latestAction: [[], [], []],
|
latestAction: [[], [], [], []],
|
||||||
currentPlayer: null, // index of current player
|
currentPlayer: null, // index of current player
|
||||||
turn: 0,
|
turn: 0,
|
||||||
});
|
});
|
||||||
|
@ -961,47 +1022,12 @@ function PvEDoudizhuDemoView() {
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
<Layout.Col span="7" style={{ height: '100%' }}>
|
<Layout.Col span="7" style={{ height: '100%' }}>
|
||||||
<Paper className={'doudizhu-probability-paper'} elevation={3}>
|
<Paper className={'doudizhu-probability-paper'} elevation={3}>
|
||||||
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
|
||||||
<div style={{ padding: '16px' }}>
|
|
||||||
<span style={{ textAlign: 'center', marginBottom: '8px', display: 'block' }}>
|
|
||||||
{t('doudizhu.three_landlord_cards')}
|
|
||||||
</span>
|
|
||||||
<div className="playingCards" style={{ display: 'flex', justifyContent: 'center' }}>
|
|
||||||
{sortDoudizhuCards(originalThreeLandlordCards, true).map((card) => {
|
|
||||||
const [rankClass, suitClass, rankText, suitText] = translateCardData(card);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={'probability-cards-' + rankText + '-' + suitText}
|
|
||||||
style={{ fontSize: '1.2em' }}
|
|
||||||
className={`card ${rankClass} full-content ${suitClass}`}
|
|
||||||
>
|
|
||||||
<span className="rank">{rankText}</span>
|
|
||||||
<span className="suit">{suitText}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
height: '112px',
|
|
||||||
padding: '16px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>{t('waiting...')}</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<Divider />
|
|
||||||
<div className={'probability-player'} style={{ height: '19px', textAlign: 'center' }}>
|
<div className={'probability-player'} style={{ height: '19px', textAlign: 'center' }}>
|
||||||
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
||||||
<span>
|
<span>
|
||||||
{t(
|
{t(
|
||||||
`doudizhu.${
|
`doudizhu.${
|
||||||
['landlord', 'landlord_down', 'landlord_up'][
|
['landlord', 'landlord_down', 'landlord_front', 'landlord_up'][
|
||||||
playerInfo[gameState.currentPlayer].douzeroPlayerPosition
|
playerInfo[gameState.currentPlayer].douzeroPlayerPosition
|
||||||
]
|
]
|
||||||
}`,
|
}`,
|
||||||
|
@ -1016,6 +1042,7 @@ function PvEDoudizhuDemoView() {
|
||||||
<div className={'probability-item'}>{computeProbabilityItem(0)}</div>
|
<div className={'probability-item'}>{computeProbabilityItem(0)}</div>
|
||||||
<div className={'probability-item'}>{computeProbabilityItem(1)}</div>
|
<div className={'probability-item'}>{computeProbabilityItem(1)}</div>
|
||||||
<div className={'probability-item'}>{computeProbabilityItem(2)}</div>
|
<div className={'probability-item'}>{computeProbabilityItem(2)}</div>
|
||||||
|
<div className={'probability-item'}>{computeProbabilityItem(3)}</div>
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Layout.Col>
|
</Layout.Col>
|
||||||
|
|
Loading…
Reference in New Issue