更新为四人版本
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,
|
||||
11: 8, 12: 9, 13: 10, 14: 11, 17: 12, 20: 13, 30: 14}
|
||||
|
||||
NumOnes2Array = {0: np.array([0, 0, 0, 0]),
|
||||
1: np.array([1, 0, 0, 0]),
|
||||
2: np.array([1, 1, 0, 0]),
|
||||
3: np.array([1, 1, 1, 0]),
|
||||
4: np.array([1, 1, 1, 1])}
|
||||
NumOnes2Array = {0: np.array([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
1: np.array([1, 0, 0, 0, 0, 0, 0, 0]),
|
||||
2: np.array([1, 1, 0, 0, 0, 0, 0, 0]),
|
||||
3: np.array([1, 1, 1, 0, 0, 0, 0, 0]),
|
||||
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)
|
||||
one_hot[bomb_num] = 1
|
||||
|
||||
def _get_one_hot_bomb(bomb_num, use_legacy = False):
|
||||
"""
|
||||
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
|
||||
|
||||
def _load_model(position, model_dir, use_onnx):
|
||||
if not use_onnx or not os.path.isfile(os.path.join(model_dir, position+'.onnx')) :
|
||||
from models import model_dict
|
||||
model = model_dict[position]()
|
||||
from models import model_dict_new
|
||||
model = model_dict_new[position]()
|
||||
model_state_dict = model.state_dict()
|
||||
model_path = os.path.join(model_dir, position+'.ckpt')
|
||||
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'))
|
||||
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()
|
||||
if new_model:
|
||||
sequence = sequence[::-1]
|
||||
if len(sequence) < length:
|
||||
empty_sequence = [[] for _ in range(length - len(sequence))]
|
||||
empty_sequence.extend(sequence)
|
||||
|
@ -74,18 +96,22 @@ class DeepAgent:
|
|||
|
||||
def cards2array(self, list_cards):
|
||||
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)
|
||||
jokers = np.zeros(2, dtype=np.float32)
|
||||
matrix = np.zeros([8, 13], dtype=np.int8)
|
||||
jokers = np.zeros(4, dtype=np.int8)
|
||||
counter = Counter(list_cards)
|
||||
for card, num_times in counter.items():
|
||||
if card < 20:
|
||||
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
||||
elif card == 20:
|
||||
jokers[0] = 1
|
||||
elif card == 30:
|
||||
if num_times == 2:
|
||||
jokers[1] = 1
|
||||
elif card == 30:
|
||||
jokers[2] = 1
|
||||
if num_times == 2:
|
||||
jokers[3] = 1
|
||||
return np.concatenate((matrix.flatten('F'), jokers))
|
||||
|
||||
def get_one_hot_array(self, num_left_cards, max_num_cards):
|
||||
|
@ -94,162 +120,135 @@ class DeepAgent:
|
|||
|
||||
return one_hot
|
||||
|
||||
def action_seq_list2array(self, action_seq_list):
|
||||
action_seq_array = np.zeros((len(action_seq_list), 54), dtype=np.float32)
|
||||
def action_seq_list2array(self, action_seq_list, new_model=True):
|
||||
"""
|
||||
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):
|
||||
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 = action_seq_array.reshape(5, 162)
|
||||
action_seq_array = action_seq_array.reshape(5, 432)
|
||||
return action_seq_array
|
||||
|
||||
def act(self, infoset):
|
||||
player_position = infoset.player_position
|
||||
def get_obs_general(self, infoset, position, use_legacy = False):
|
||||
num_legal_actions = len(infoset.legal_actions)
|
||||
my_handcards = self.cards2array(infoset.player_hand_cards)
|
||||
my_handcards_batch = np.repeat(my_handcards[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
my_action_batch[j, :] = self.cards2array(action)
|
||||
|
||||
last_action = self.cards2array(infoset.rival_move)
|
||||
last_action_batch = np.repeat(last_action[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
landlord_num_cards_left = self.get_one_hot_array(
|
||||
infoset.num_cards_left_dict['landlord'], 33)
|
||||
|
||||
if player_position == 0:
|
||||
landlord_up_num_cards_left = self.get_one_hot_array(
|
||||
infoset.num_cards_left[2], 17)
|
||||
landlord_up_num_cards_left_batch = np.repeat(
|
||||
landlord_up_num_cards_left[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
infoset.num_cards_left_dict['landlord_up'], 25)
|
||||
|
||||
landlord_front_num_cards_left = self.get_one_hot_array(
|
||||
infoset.num_cards_left_dict['landlord_front'], 25)
|
||||
|
||||
landlord_down_num_cards_left = self.get_one_hot_array(
|
||||
infoset.num_cards_left[1], 17)
|
||||
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)
|
||||
infoset.num_cards_left_dict['landlord_down'], 25)
|
||||
|
||||
landlord_played_cards = self.cards2array(
|
||||
infoset.played_cards[0])
|
||||
landlord_played_cards_batch = np.repeat(
|
||||
landlord_played_cards[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
|
||||
if player_position == 2:
|
||||
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(
|
||||
landlord_up_played_cards = self.cards2array(
|
||||
infoset.played_cards[1])
|
||||
teammate_played_cards_batch = np.repeat(
|
||||
teammate_played_cards[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
|
||||
else:
|
||||
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(
|
||||
landlord_front_played_cards = self.cards2array(
|
||||
infoset.played_cards[2])
|
||||
teammate_played_cards_batch = np.repeat(
|
||||
teammate_played_cards[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
|
||||
landlord_down_played_cards = self.cards2array(
|
||||
infoset.played_cards[3])
|
||||
|
||||
bomb_num = _get_one_hot_bomb(
|
||||
infoset.bomb_num)
|
||||
infoset.bomb_num, use_legacy)
|
||||
bomb_num_batch = np.repeat(
|
||||
bomb_num[np.newaxis, :],
|
||||
num_legal_actions, axis=0)
|
||||
x_batch = np.hstack((my_handcards_batch,
|
||||
other_handcards_batch,
|
||||
landlord_played_cards_batch,
|
||||
teammate_played_cards_batch,
|
||||
last_action_batch,
|
||||
last_landlord_action_batch,
|
||||
last_teammate_action_batch,
|
||||
landlord_num_cards_left_batch,
|
||||
teammate_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(
|
||||
num_cards_left = np.hstack((
|
||||
landlord_num_cards_left, # 33
|
||||
landlord_up_num_cards_left, # 25
|
||||
landlord_front_num_cards_left, # 25
|
||||
landlord_down_num_cards_left))
|
||||
|
||||
if use_legacy:
|
||||
x_batch = np.hstack((
|
||||
bid_info_batch, # 20
|
||||
multiply_info_batch)) # 4
|
||||
x_no_action = np.hstack((
|
||||
bid_info,
|
||||
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, :, :],
|
||||
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:
|
||||
ort_inputs = {'z': z_batch, 'x': x_batch}
|
||||
y_pred = self.model.run(None, ort_inputs)[0]
|
||||
|
@ -259,7 +258,7 @@ class DeepAgent:
|
|||
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())
|
||||
torch.from_numpy(x_batch).float())['values']
|
||||
y_pred = y_pred.detach().numpy()
|
||||
|
||||
y_pred = y_pred.flatten()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import torch
|
||||
from torch import nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
class LandlordLstmModel(nn.Module):
|
||||
def __init__(self):
|
||||
|
@ -61,7 +62,137 @@ class FarmerLstmModel(nn.Module):
|
|||
x = self.dense6(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['landlord'] = LandlordLstmModel
|
||||
model_dict['landlord_up'] = 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'
|
||||
device = torch.device('cpu')
|
||||
players = []
|
||||
for i in range(3):
|
||||
for i in range(4):
|
||||
model_path = os.path.join(pretrained_dir, str(i)+'.pth')
|
||||
agent = torch.load(model_path, map_location=device)
|
||||
agent.set_device(device)
|
||||
|
@ -50,33 +50,38 @@ def predict():
|
|||
try:
|
||||
# Player postion
|
||||
player_position = request.form.get('player_position')
|
||||
if player_position not in ['0', '1', '2']:
|
||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, or 2'})
|
||||
if player_position not in ['0', '1', '2', '3']:
|
||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, 2 or 3'})
|
||||
player_position = int(player_position)
|
||||
|
||||
# Player hand cards
|
||||
player_hand_cards = ''.join(
|
||||
[RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')])
|
||||
if player_position == 0:
|
||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 20:
|
||||
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-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-33'})
|
||||
else:
|
||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 17:
|
||||
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-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-25'})
|
||||
|
||||
# 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):
|
||||
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'})
|
||||
|
||||
# Three landlord cards
|
||||
three_landlord_cards = ''.join(
|
||||
[RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')])
|
||||
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'})
|
||||
# three_landlord_cards = ''.join(
|
||||
# [RealCard2EnvCard[c] for c in request.form.get('three_landlord_cards')])
|
||||
# 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'})
|
||||
|
||||
# Card play sequence
|
||||
if request.form.get('card_play_action_seq') == '':
|
||||
|
@ -89,7 +94,7 @@ def predict():
|
|||
tmp_seq[i] = 'pass'
|
||||
card_play_action_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 = ''.join(
|
||||
|
@ -99,7 +104,7 @@ def predict():
|
|||
|
||||
# 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(
|
||||
''.join([RealCard2EnvCard[c] for c in request.form.get(field)]))
|
||||
|
||||
|
@ -110,7 +115,7 @@ def predict():
|
|||
state['num_cards_left'] = num_cards_left
|
||||
state['others_hand'] = other_hand_cards
|
||||
state['played_cards'] = played_cards
|
||||
state['seen_cards'] = three_landlord_cards
|
||||
# state['seen_cards'] = three_landlord_cards
|
||||
state['self'] = player_position
|
||||
state['trace'] = card_play_action_seq
|
||||
|
||||
|
@ -118,6 +123,9 @@ def predict():
|
|||
rival_move = 'pass'
|
||||
if len(card_play_action_seq) != 0:
|
||||
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]
|
||||
else:
|
||||
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)
|
||||
|
||||
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 += 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:
|
||||
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:
|
||||
all_moves = mg.gen_type_7_3_2()
|
||||
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)
|
||||
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:
|
||||
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)
|
||||
|
||||
elif rival_move_type == md.TYPE_13_4_2:
|
||||
all_moves = mg.gen_type_13_4_2()
|
||||
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 rival_move_type != md.TYPE_0_PASS and rival_move_type < md.TYPE_4_BOMB:
|
||||
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()
|
||||
|
||||
if len(rival_move) != 0: # rival_move is not 'pass'
|
||||
moves = moves + [[]]
|
||||
|
@ -355,37 +367,39 @@ def _get_legal_card_play_actions(player_hand_cards, rival_move):
|
|||
moves.sort()
|
||||
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
|
||||
|
||||
|
||||
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}
|
||||
|
||||
NumOnes2Array = {0: np.array([0, 0, 0, 0]),
|
||||
1: np.array([1, 0, 0, 0]),
|
||||
2: np.array([1, 1, 0, 0]),
|
||||
3: np.array([1, 1, 1, 0]),
|
||||
4: np.array([1, 1, 1, 1])}
|
||||
NumOnes2Array = {0: np.array([0, 0, 0, 0, 0, 0, 0, 0]),
|
||||
1: np.array([1, 0, 0, 0, 0, 0, 0, 0]),
|
||||
2: np.array([1, 1, 0, 0, 0, 0, 0, 0]),
|
||||
3: np.array([1, 1, 1, 0, 0, 0, 0, 0]),
|
||||
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):
|
||||
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)
|
||||
jokers = np.zeros(2, dtype=np.int8)
|
||||
jokers = np.zeros(4, dtype=np.int8)
|
||||
counter = Counter(cards)
|
||||
for card, num_times in counter.items():
|
||||
if card == 'B':
|
||||
jokers[0] = 1
|
||||
elif card == 'R':
|
||||
if num_times == 2:
|
||||
jokers[1] = 1
|
||||
elif card == 'R':
|
||||
jokers[2] = 1
|
||||
if num_times == 2:
|
||||
jokers[3] = 1
|
||||
else:
|
||||
matrix[:, Card2Column[card]] = NumOnes2Array[num_times]
|
||||
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):
|
||||
action_seq_array = np.zeros((len(action_seq_list), 54), np.int8)
|
||||
for row, cards in enumerate(action_seq_list):
|
||||
action_seq_array[row, :] = _cards2array(cards)
|
||||
action_seq_array = action_seq_array.flatten()
|
||||
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):
|
||||
if list_cards != []:
|
||||
action_seq_array[row, :108] = _cards2array(list_cards[1])
|
||||
return action_seq_array
|
||||
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ RealCard2EnvCard = {'3': 3, '4': 4, '5': 5, '6': 6, '7': 7,
|
|||
|
||||
pretrained_dir = 'pretrained/douzero_pretrained'
|
||||
players = []
|
||||
for position in ['landlord', 'landlord_down', 'landlord_up']:
|
||||
players.append(DeepAgent(position, pretrained_dir, use_onnx=True))
|
||||
for position in ['landlord', 'landlord_down', 'landlord_front', 'landlord_up']:
|
||||
players.append(DeepAgent(position, pretrained_dir, use_onnx=False))
|
||||
|
||||
@app.route('/predict', methods=['POST'])
|
||||
def predict():
|
||||
|
@ -29,30 +29,36 @@ def predict():
|
|||
try:
|
||||
# Player postion
|
||||
player_position = request.form.get('player_position')
|
||||
if player_position not in ['0', '1', '2']:
|
||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, or 2'})
|
||||
if player_position not in ['0', '1', '2', '3']:
|
||||
return jsonify({'status': 1, 'message': 'player_position must be 0, 1, 2 or 3'})
|
||||
player_position = int(player_position)
|
||||
|
||||
# Player hand cards
|
||||
player_hand_cards = [RealCard2EnvCard[c] for c in request.form.get('player_hand_cards')]
|
||||
if player_position == 0:
|
||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 20:
|
||||
return jsonify({'status': 2, 'message': 'the number of hand cards should be 1-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-33'})
|
||||
else:
|
||||
if len(player_hand_cards) < 1 or len(player_hand_cards) > 17:
|
||||
return jsonify({'status': 3, 'message': 'the number of hand cards should be 1-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-25'})
|
||||
|
||||
# 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):
|
||||
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'})
|
||||
|
||||
# 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:
|
||||
return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
||||
# 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:
|
||||
# return jsonify({'status': 6, 'message': 'the number of landlord cards should be 0-3'})
|
||||
|
||||
# Card play sequence
|
||||
if request.form.get('card_play_action_seq') == '':
|
||||
|
@ -67,33 +73,46 @@ def predict():
|
|||
|
||||
# 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)])
|
||||
|
||||
# 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)])
|
||||
|
||||
# Bomb Num
|
||||
bomb_num = int(request.form.get('bomb_num'))
|
||||
bomb_num = request.form.get('bomb_num')
|
||||
|
||||
# InfoSet
|
||||
info_set = InfoSet()
|
||||
info_set.player_position = player_position
|
||||
info_set.player_hand_cards = player_hand_cards
|
||||
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.other_hand_cards = other_hand_cards
|
||||
info_set.last_moves = last_moves
|
||||
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
|
||||
rival_move = []
|
||||
if len(card_play_action_seq) != 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]
|
||||
else:
|
||||
rival_move = card_play_action_seq[-1]
|
||||
|
@ -154,7 +173,9 @@ class InfoSet(object):
|
|||
self.player_position = None
|
||||
self.player_hand_cards = 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.other_hand_cards = 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)
|
||||
|
||||
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 += 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:
|
||||
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:
|
||||
all_moves = mg.gen_type_7_3_2()
|
||||
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)
|
||||
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:
|
||||
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)
|
||||
|
||||
elif rival_move_type == md.TYPE_13_4_2:
|
||||
all_moves = mg.gen_type_13_4_2()
|
||||
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 rival_move_type != md.TYPE_0_PASS and rival_move_type < md.TYPE_4_BOMB:
|
||||
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()
|
||||
|
||||
if len(rival_move) != 0: # rival_move is not 'pass'
|
||||
moves = moves + [[]]
|
||||
|
|
|
@ -24,8 +24,8 @@ def get_move_type(move):
|
|||
if move_size == 2:
|
||||
if move[0] == move[1]:
|
||||
return {'type': TYPE_2_PAIR, 'rank': move[0]}
|
||||
elif move == [20, 30]: # Kings
|
||||
return {'type': TYPE_5_KING_BOMB}
|
||||
# elif move == [20, 30]: # Kings
|
||||
# return {'type': TYPE_5_KING_BOMB}
|
||||
else:
|
||||
return {'type': TYPE_15_WRONG}
|
||||
|
||||
|
@ -39,8 +39,10 @@ def get_move_type(move):
|
|||
if len(move_dict) == 1:
|
||||
return {'type': TYPE_4_BOMB, 'rank': move[0]}
|
||||
elif len(move_dict) == 2:
|
||||
if move[0] == move[1] == move[2] or move[1] == move[2] == move[3]:
|
||||
return {'type': TYPE_6_3_1, 'rank': move[1]}
|
||||
if move[0] == 20 and move[2] == 30: # Kings
|
||||
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:
|
||||
return {'type': TYPE_15_WRONG}
|
||||
else:
|
||||
|
@ -50,7 +52,9 @@ def get_move_type(move):
|
|||
return {'type': TYPE_8_SERIAL_SINGLE, 'rank': move[0], 'len': len(move)}
|
||||
|
||||
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]}
|
||||
else:
|
||||
return {'type': TYPE_15_WRONG}
|
||||
|
@ -60,13 +64,21 @@ def get_move_type(move):
|
|||
count_dict[n] += 1
|
||||
|
||||
if move_size == 6:
|
||||
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 len(move_dict) == 1:
|
||||
return {'type': TYPE_4_BOMB6, 'rank': move[0]}
|
||||
# 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
|
||||
(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])}
|
||||
# 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):
|
||||
# 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())
|
||||
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()
|
||||
if is_continuous_seq(serial_3):
|
||||
if len(serial_3) == len(single)+len(pair)*2:
|
||||
return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3)}
|
||||
# if len(serial_3) == len(single)+len(pair)*2:
|
||||
# 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:
|
||||
return {'type': TYPE_12_SERIAL_3_2, 'rank': serial_3[0], 'len': len(serial_3)}
|
||||
|
||||
if len(serial_3) == 4:
|
||||
if is_continuous_seq(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]):
|
||||
return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3) - 1}
|
||||
# if len(serial_3) == 4:
|
||||
# if is_continuous_seq(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]):
|
||||
# return {'type': TYPE_11_SERIAL_3_1, 'rank': serial_3[0], 'len': len(serial_3) - 1}
|
||||
|
||||
return {'type': TYPE_15_WRONG}
|
||||
|
|
|
@ -3,7 +3,9 @@ import collections
|
|||
import itertools
|
||||
|
||||
class MovesGener(object):
|
||||
|
||||
"""
|
||||
This is for generating the possible combinations
|
||||
"""
|
||||
def __init__(self, cards_list):
|
||||
self.cards_list = cards_list
|
||||
self.cards_dict = collections.defaultdict(int)
|
||||
|
@ -89,17 +91,17 @@ class MovesGener(object):
|
|||
self.triple_cards_moves.append([k, k, k])
|
||||
return self.triple_cards_moves
|
||||
|
||||
def gen_type_4_bomb(self):
|
||||
def gen_type_4_bomb(self, num = 4):
|
||||
self.bomb_moves = []
|
||||
for k, v in self.cards_dict.items():
|
||||
if v == 4:
|
||||
self.bomb_moves.append([k, k, k, k])
|
||||
if v == num:
|
||||
self.bomb_moves.append([k] * num)
|
||||
return self.bomb_moves
|
||||
|
||||
def gen_type_5_king_bomb(self):
|
||||
self.final_bomb_moves = []
|
||||
if 20 in self.cards_list and 30 in self.cards_list:
|
||||
self.final_bomb_moves.append([20, 30])
|
||||
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, 20, 30, 30])
|
||||
return self.final_bomb_moves
|
||||
|
||||
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_2_pair())
|
||||
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_6_3_1())
|
||||
# moves.extend(self.gen_type_6_3_1())
|
||||
moves.extend(self.gen_type_7_3_2())
|
||||
moves.extend(self.gen_type_8_serial_single())
|
||||
moves.extend(self.gen_type_9_serial_pair())
|
||||
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_13_4_2())
|
||||
moves.extend(self.gen_type_14_4_22())
|
||||
# moves.extend(self.gen_type_13_4_2())
|
||||
# moves.extend(self.gen_type_14_4_22())
|
||||
return moves
|
||||
|
|
|
@ -9,7 +9,6 @@ def common_handle(moves, rival_move):
|
|||
new_moves.append(move)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_1_single(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):
|
||||
return common_handle(moves, rival_move)
|
||||
|
||||
|
||||
# No need to filter for type_5_king_bomb
|
||||
|
||||
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)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_7_3_2(moves, rival_move):
|
||||
rival_move.sort()
|
||||
rival_rank = rival_move[2]
|
||||
|
@ -51,19 +48,15 @@ def filter_type_7_3_2(moves, rival_move):
|
|||
new_moves.append(move)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_8_serial_single(moves, rival_move):
|
||||
return common_handle(moves, rival_move)
|
||||
|
||||
|
||||
def filter_type_9_serial_pair(moves, rival_move):
|
||||
return common_handle(moves, rival_move)
|
||||
|
||||
|
||||
def filter_type_10_serial_triple(moves, rival_move):
|
||||
return common_handle(moves, rival_move)
|
||||
|
||||
|
||||
def filter_type_11_serial_3_1(moves, rival_move):
|
||||
rival = collections.Counter(rival_move)
|
||||
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)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_12_serial_3_2(moves, rival_move):
|
||||
rival = collections.Counter(rival_move)
|
||||
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)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_13_4_2(moves, rival_move):
|
||||
rival_move.sort()
|
||||
rival_rank = rival_move[2]
|
||||
|
@ -99,7 +90,6 @@ def filter_type_13_4_2(moves, rival_move):
|
|||
new_moves.append(move)
|
||||
return new_moves
|
||||
|
||||
|
||||
def filter_type_14_4_22(moves, rival_move):
|
||||
rival = collections.Counter(rival_move)
|
||||
rival_rank = my_rank = 0
|
||||
|
|
|
@ -10,17 +10,22 @@ TYPE_0_PASS = 0
|
|||
TYPE_1_SINGLE = 1
|
||||
TYPE_2_PAIR = 2
|
||||
TYPE_3_TRIPLE = 3
|
||||
TYPE_4_BOMB = 4
|
||||
TYPE_5_KING_BOMB = 5
|
||||
TYPE_4_BOMB = 44
|
||||
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_7_3_2 = 7
|
||||
TYPE_8_SERIAL_SINGLE = 8
|
||||
TYPE_9_SERIAL_PAIR = 9
|
||||
TYPE_10_SERIAL_TRIPLE = 10
|
||||
TYPE_11_SERIAL_3_1 = 11
|
||||
# TYPE_11_SERIAL_3_1 = 11
|
||||
TYPE_12_SERIAL_3_2 = 12
|
||||
TYPE_13_4_2 = 13
|
||||
TYPE_14_4_22 = 14
|
||||
# TYPE_13_4_2 = 13
|
||||
# TYPE_14_4_22 = 14
|
||||
TYPE_15_WRONG = 15
|
||||
|
||||
# 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(12) { left: 15.4em; }
|
||||
.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(15) { left: 19.6em; }
|
||||
.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(25) { left: 33.6em; }
|
||||
.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(2) { left: 1.1em; }
|
||||
|
@ -684,6 +690,13 @@
|
|||
.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(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 */
|
||||
.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(31) { left: 60px; bottom: 30px; }
|
||||
.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 {
|
||||
font-size: 16px;
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
div {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -118,30 +118,32 @@
|
|||
#left-player {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
top: 80px;
|
||||
|
||||
.player-main-area {
|
||||
width: 300px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
font-size: 10px;
|
||||
|
||||
.player-hand-up {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-left: 150px;
|
||||
margin-left: 10px;
|
||||
margin-top: 100px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.player-hand-down {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
margin-left: 150px;
|
||||
margin-left: 10px;
|
||||
margin-top: 100px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.player-hand-placeholder {
|
||||
width: 150px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-left: 130px;
|
||||
|
@ -150,38 +152,40 @@
|
|||
|
||||
.played-card-area {
|
||||
position: relative;
|
||||
left: 20px;
|
||||
top: 20px;
|
||||
left: 50px;
|
||||
top: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
#right-player {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
top: 80px;
|
||||
|
||||
.player-main-area {
|
||||
width: 300px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
font-size: 10px;
|
||||
|
||||
.player-hand-up {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin-right: 150px;
|
||||
margin-top: 100px;
|
||||
margin-right: 10px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.player-hand-down {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
margin-top: 100px;
|
||||
margin-right: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.player-hand-placeholder {
|
||||
width: 150px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
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 {
|
||||
position: relative;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,29 +250,29 @@
|
|||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 50%;
|
||||
margin-left: -290px;
|
||||
margin-left: -340px;
|
||||
|
||||
.player-main-area {
|
||||
width: 580px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
position: relative;
|
||||
|
||||
.player-hand {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding-left: 20px;
|
||||
margin-left: 130px;
|
||||
margin-left: 80px;
|
||||
width: 450px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.player-hand-placeholder {
|
||||
width: 150px;
|
||||
height: 130px;
|
||||
height: 80px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding-left: 20px;
|
||||
margin-left: 130px;
|
||||
margin-left: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,9 +163,9 @@ class DoudizhuGameBoard extends React.Component {
|
|||
computeSideHand(cards) {
|
||||
let upCards;
|
||||
let downCards = [];
|
||||
if (cards.length > 10) {
|
||||
upCards = cards.slice(0, 10);
|
||||
downCards = cards.slice(10);
|
||||
if (cards.length > 16) {
|
||||
upCards = cards.slice(0, 16);
|
||||
downCards = cards.slice(16);
|
||||
} else {
|
||||
upCards = cards;
|
||||
}
|
||||
|
@ -320,11 +320,13 @@ class DoudizhuGameBoard extends React.Component {
|
|||
return element.id === bottomId;
|
||||
});
|
||||
const bottomIdx = found ? found.index : -1;
|
||||
const rightIdx = bottomIdx >= 0 ? (bottomIdx + 1) % 3 : -1;
|
||||
const leftIdx = rightIdx >= 0 ? (rightIdx + 1) % 3 : -1;
|
||||
const rightIdx = bottomIdx >= 0 ? (bottomIdx + 1) % 4 : -1;
|
||||
const topIdx = rightIdx >= 0 ? (rightIdx + 1) % 4 : -1;
|
||||
const leftIdx = topIdx >= 0 ? (topIdx + 1) % 4 : -1;
|
||||
let rightId = -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) => {
|
||||
return element.index === rightIdx;
|
||||
});
|
||||
|
@ -333,6 +335,10 @@ class DoudizhuGameBoard extends React.Component {
|
|||
return element.index === leftIdx;
|
||||
});
|
||||
if (found) leftId = found.id;
|
||||
found = this.props.playerInfo.find((element) => {
|
||||
return element.index === topIdx;
|
||||
});
|
||||
if (found) topId = found.id;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
|
@ -381,6 +387,21 @@ class DoudizhuGameBoard extends React.Component {
|
|||
</div>
|
||||
<div className="played-card-area">{rightIdx >= 0 ? this.playerDecisionArea(rightIdx) : ''}</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 className="played-card-area">
|
||||
{bottomIdx >= 0 ? this.playerDecisionArea(bottomIdx) : ''}
|
||||
|
@ -438,7 +459,12 @@ class DoudizhuGameBoard extends React.Component {
|
|||
</Button>
|
||||
<Button
|
||||
onClick={() => this.props.handleSelectRole('landlord_down')}
|
||||
style={{ width: '225px', justifyContent: 'space-evenly' }}
|
||||
style={{
|
||||
width: '225px',
|
||||
justifyContent: 'space-evenly',
|
||||
marginTop: '20px',
|
||||
marginBottom: '20px',
|
||||
}}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
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')}
|
||||
<br />({t('doudizhu.landlord_down')})
|
||||
</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>
|
||||
)}
|
||||
{this.props.gamePlayable && this.props.gameStatus === 'localeSelection' && (
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"play_as_landlord": "扮演地主",
|
||||
"play_as_peasant": "扮演农民",
|
||||
"landlord_up": "地主上家",
|
||||
"landlord_front": "地主对家",
|
||||
"landlord_down": "地主下家",
|
||||
"peasants_win": "农民胜利!",
|
||||
"landlord_win": "地主胜利!",
|
||||
|
|
|
@ -51,12 +51,12 @@ export function translateCardData(card) {
|
|||
let rankText;
|
||||
let suitText = '';
|
||||
// translate rank
|
||||
if (card === 'RJ') {
|
||||
if (card === 'RJ' || card === 'RJ2') {
|
||||
rankClass = 'big';
|
||||
rankText = '+';
|
||||
suitClass = 'joker';
|
||||
suitText = 'Joker';
|
||||
} else if (card === 'BJ') {
|
||||
} else if (card === 'BJ' || card === 'BJ2') {
|
||||
rankClass = 'little';
|
||||
rankText = '-';
|
||||
suitClass = 'joker';
|
||||
|
@ -67,7 +67,7 @@ export function translateCardData(card) {
|
|||
rankText = card.charAt(1) === 'T' ? `10` : card.charAt(1);
|
||||
}
|
||||
// translate suitClass
|
||||
if (card !== 'RJ' && card !== 'BJ') {
|
||||
if (card !== 'RJ' && card !== 'BJ' && card !== 'RJ2' && card !== 'BJ2') {
|
||||
suitClass = suitMap.get(card.charAt(0));
|
||||
suitText = suitMapSymbol.get(card.charAt(0));
|
||||
}
|
||||
|
@ -101,9 +101,9 @@ export function computeHandCardsWidth(num, emWidth) {
|
|||
}
|
||||
|
||||
export function card2SuiteAndRank(card) {
|
||||
if (card === 'BJ' || card === 'B') {
|
||||
if (card === 'BJ' || card === 'B' || card === 'BJ2' || card === 'B2') {
|
||||
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' };
|
||||
} else {
|
||||
return { suite: card[0], rank: card[1] };
|
||||
|
@ -165,6 +165,60 @@ export const fullDoudizhuDeck = [
|
|||
'C3',
|
||||
'H3',
|
||||
'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 = {
|
||||
|
@ -222,6 +276,60 @@ export const fullDoudizhuDeckIndex = {
|
|||
C3: 3,
|
||||
H3: 2,
|
||||
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) {
|
||||
|
@ -234,10 +342,38 @@ export function sortDoudizhuCards(cards, ascending = false) {
|
|||
}
|
||||
|
||||
export function isDoudizhuBomb(cards) {
|
||||
if (cards.length === 2) return (cards[0] === 'RJ' && cards[1] === 'BJ') || (cards[0] === 'BJ' && cards[1] === 'RJ');
|
||||
if (cards.length === 4)
|
||||
return cards[0][1] === cards[1][1] && cards[0][1] === cards[2][1] && cards[0][1] === cards[3][1];
|
||||
return false;
|
||||
if(cards.length <= 3 || cards.length >= 9)
|
||||
return -1;
|
||||
const distinctedCards = [...new Set(cards.map(x=>x.substring(0, 2)))];
|
||||
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) {
|
||||
|
|
|
@ -33,7 +33,7 @@ import { douzeroDemoUrl } from '../../utils/config';
|
|||
|
||||
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();
|
||||
|
||||
const initConsiderationTime = 30000;
|
||||
|
@ -42,9 +42,10 @@ const mainPlayerId = 0; // index of main player (for the sake of simplify code l
|
|||
let playerInfo = [];
|
||||
|
||||
let initHands = [
|
||||
shuffledDoudizhuDeck.slice(3, 20),
|
||||
shuffledDoudizhuDeck.slice(20, 37),
|
||||
shuffledDoudizhuDeck.slice(37, 54),
|
||||
shuffledDoudizhuDeck.slice(8, 33),
|
||||
shuffledDoudizhuDeck.slice(33, 58),
|
||||
shuffledDoudizhuDeck.slice(58, 83),
|
||||
shuffledDoudizhuDeck.slice(83, 108),
|
||||
];
|
||||
|
||||
console.log('init hands', initHands);
|
||||
|
@ -53,12 +54,14 @@ console.log('three landlord card', threeLandlordCards);
|
|||
let gameStateTimeout = null;
|
||||
|
||||
let gameHistory = [];
|
||||
let bombNum = 0;
|
||||
let bombNum = [0, 0 , 0];
|
||||
let lastMoveLandlord = [];
|
||||
let lastMoveLandlordDown = [];
|
||||
let lastMoveLandlordFront = [];
|
||||
let lastMoveLandlordUp = [];
|
||||
let playedCardsLandlord = [];
|
||||
let playedCardsLandlordDown = [];
|
||||
let playedCardsLandlordFront = [];
|
||||
let playedCardsLandlordUp = [];
|
||||
let legalActions = { turn: -1, actions: [] };
|
||||
let hintIdx = -1;
|
||||
|
@ -73,8 +76,8 @@ function PvEDoudizhuDemoView() {
|
|||
const [toggleFade, setToggleFade] = useState('');
|
||||
const [gameStatus, setGameStatus] = useState(localStorage.getItem('LOCALE') ? 'ready' : 'localeSelection'); // "localeSelection", "ready", "playing", "paused", "over"
|
||||
const [gameState, setGameState] = useState({
|
||||
hands: [[], [], []],
|
||||
latestAction: [[], [], []],
|
||||
hands: [[], [], [], []],
|
||||
latestAction: [[], [], [], []],
|
||||
currentPlayer: null, // index of current player
|
||||
turn: 0,
|
||||
});
|
||||
|
@ -90,8 +93,8 @@ function PvEDoudizhuDemoView() {
|
|||
const cardArr2DouzeroFormat = (cards) => {
|
||||
return cards
|
||||
.map((card) => {
|
||||
if (card === 'RJ') return 'D';
|
||||
if (card === 'BJ') return 'X';
|
||||
if (card === 'RJ' || card === 'RJ2') return 'D';
|
||||
if (card === 'BJ' || card === 'BJ2') return 'X';
|
||||
return card[1];
|
||||
})
|
||||
.join('');
|
||||
|
@ -122,12 +125,15 @@ function PvEDoudizhuDemoView() {
|
|||
}
|
||||
|
||||
// if next player is user, get legal actions
|
||||
if ((gameState.currentPlayer + 1) % 3 === mainPlayerId) {
|
||||
if ((gameState.currentPlayer + 1) % 4 === mainPlayerId) {
|
||||
hintIdx = -1;
|
||||
const player_hand_cards = cardArr2DouzeroFormat(gameState.hands[mainPlayerId].slice().reverse());
|
||||
let rival_move = '';
|
||||
if (playingCard.length === 0) {
|
||||
rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 1], true));
|
||||
if(rival_move === '') {
|
||||
rival_move = cardArr2DouzeroFormat(sortDoudizhuCards(gameHistory[gameHistory.length - 2], true));
|
||||
}
|
||||
} else {
|
||||
rival_move = rankOnly ? playingCard.join('') : cardArr2DouzeroFormat(playingCard);
|
||||
}
|
||||
|
@ -142,7 +148,7 @@ function PvEDoudizhuDemoView() {
|
|||
actions: data.legal_action.split(','),
|
||||
};
|
||||
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
|
||||
|
@ -200,6 +206,10 @@ function PvEDoudizhuDemoView() {
|
|||
playedCardsLandlordDown = playedCardsLandlordDown.concat(newHistoryRecord);
|
||||
break;
|
||||
case 2:
|
||||
lastMoveLandlordFront = newHistoryRecord;
|
||||
playedCardsLandlordFront = playedCardsLandlordFront.concat(newHistoryRecord);
|
||||
break;
|
||||
case 3:
|
||||
lastMoveLandlordUp = newHistoryRecord;
|
||||
playedCardsLandlordUp = playedCardsLandlordUp.concat(newHistoryRecord);
|
||||
break;
|
||||
|
@ -207,11 +217,12 @@ function PvEDoudizhuDemoView() {
|
|||
break;
|
||||
}
|
||||
gameHistory.push(newHistoryRecord);
|
||||
if (isDoudizhuBomb(newHistoryRecord)) bombNum++;
|
||||
const bombLevel = isDoudizhuBomb(newHistoryRecord);
|
||||
if (bombLevel >= 0) bombNum[bombLevel]++;
|
||||
|
||||
newGameState.latestAction[gameState.currentPlayer] = newLatestAction;
|
||||
newGameState.hands[gameState.currentPlayer] = newHand;
|
||||
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 3;
|
||||
newGameState.currentPlayer = (newGameState.currentPlayer + 1) % 4;
|
||||
newGameState.turn++;
|
||||
if (newHand.length === 0) {
|
||||
setGameStatus('over');
|
||||
|
@ -241,6 +252,8 @@ function PvEDoudizhuDemoView() {
|
|||
landlordWinNum: 0,
|
||||
landlordUpGameNum: 0,
|
||||
landlordUpWinNum: 0,
|
||||
landlordFrontGameNum: 0,
|
||||
landlordFrontWinNum: 0,
|
||||
landlordDownGameNum: 0,
|
||||
landlordDownWinNum: 0,
|
||||
};
|
||||
|
@ -262,6 +275,13 @@ function PvEDoudizhuDemoView() {
|
|||
}
|
||||
break;
|
||||
case 2:
|
||||
gameStatistics.landlordFrontGameNum += 1;
|
||||
if (winner.role === playerInfo[mainPlayerId].role) {
|
||||
gameStatistics.totalWinNum += 1;
|
||||
gameStatistics.landlordDownWinNum += 1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
gameStatistics.landlordUpGameNum += 1;
|
||||
if (winner.role === playerInfo[mainPlayerId].role) {
|
||||
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'),
|
||||
win: gameStatistics.landlordDownWinNum,
|
||||
|
@ -335,9 +364,11 @@ function PvEDoudizhuDemoView() {
|
|||
gameState.hands[playerInfo.find((player) => player.douzeroPlayerPosition === 0).index].length;
|
||||
const num_cards_left_landlord_down =
|
||||
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;
|
||||
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
|
||||
.map((cards) => {
|
||||
return cardArr2DouzeroFormat(cards);
|
||||
|
@ -345,18 +376,21 @@ function PvEDoudizhuDemoView() {
|
|||
.join(',');
|
||||
const other_hand_cards = cardArr2DouzeroFormat(
|
||||
sortDoudizhuCards(
|
||||
gameState.hands[(gameState.currentPlayer + 1) % 3].concat(
|
||||
gameState.hands[(gameState.currentPlayer + 2) % 3],
|
||||
gameState.hands[(gameState.currentPlayer + 1) % 4].concat(
|
||||
gameState.hands[(gameState.currentPlayer + 2) % 4],
|
||||
gameState.hands[(gameState.currentPlayer + 3) % 4],
|
||||
),
|
||||
true,
|
||||
),
|
||||
);
|
||||
const last_move_landlord = cardArr2DouzeroFormat(lastMoveLandlord.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 bomb_num = bombNum;
|
||||
const bomb_num = bombNum.join(',');
|
||||
const played_cards_landlord = cardArr2DouzeroFormat(playedCardsLandlord);
|
||||
const played_cards_landlord_down = cardArr2DouzeroFormat(playedCardsLandlordDown);
|
||||
const played_cards_landlord_front = cardArr2DouzeroFormat(playedCardsLandlordFront);
|
||||
const played_cards_landlord_up = cardArr2DouzeroFormat(playedCardsLandlordUp);
|
||||
|
||||
const requestBody = {
|
||||
|
@ -364,16 +398,19 @@ function PvEDoudizhuDemoView() {
|
|||
player_hand_cards,
|
||||
num_cards_left_landlord,
|
||||
num_cards_left_landlord_down,
|
||||
num_cards_left_landlord_front,
|
||||
num_cards_left_landlord_up,
|
||||
three_landlord_cards,
|
||||
// three_landlord_cards,
|
||||
card_play_action_seq,
|
||||
other_hand_cards,
|
||||
last_move_landlord,
|
||||
last_move_landlord_down,
|
||||
last_move_landlord_front,
|
||||
last_move_landlord_up,
|
||||
bomb_num,
|
||||
played_cards_landlord,
|
||||
played_cards_landlord_down,
|
||||
played_cards_landlord_front,
|
||||
played_cards_landlord_up,
|
||||
};
|
||||
|
||||
|
@ -396,6 +433,10 @@ function PvEDoudizhuDemoView() {
|
|||
rival_move = cardArr2DouzeroFormat(
|
||||
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 = {
|
||||
player_hand_cards,
|
||||
|
@ -500,6 +541,12 @@ function PvEDoudizhuDemoView() {
|
|||
role: 'peasant',
|
||||
douzeroPlayerPosition: -1,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
index: 3,
|
||||
role: 'peasant',
|
||||
douzeroPlayerPosition: -1,
|
||||
},
|
||||
];
|
||||
switch (role) {
|
||||
case 'landlord_up':
|
||||
|
@ -511,6 +558,10 @@ function PvEDoudizhuDemoView() {
|
|||
playerInfo[0].role = 'landlord';
|
||||
break;
|
||||
case 'landlord_down':
|
||||
playerInfo = deepCopy(playerInfoTemplate);
|
||||
playerInfo[3].role = 'landlord';
|
||||
break;
|
||||
case 'landlord_front':
|
||||
playerInfo = deepCopy(playerInfoTemplate);
|
||||
playerInfo[2].role = 'landlord';
|
||||
break;
|
||||
|
@ -519,8 +570,9 @@ function PvEDoudizhuDemoView() {
|
|||
}
|
||||
const landlordIdx = playerInfo.find((player) => player.role === 'landlord').index;
|
||||
playerInfo[landlordIdx].douzeroPlayerPosition = 0;
|
||||
playerInfo[(landlordIdx + 1) % 3].douzeroPlayerPosition = 1;
|
||||
playerInfo[(landlordIdx + 2) % 3].douzeroPlayerPosition = 2;
|
||||
playerInfo[(landlordIdx + 1) % 4].douzeroPlayerPosition = 1;
|
||||
playerInfo[(landlordIdx + 2) % 4].douzeroPlayerPosition = 2;
|
||||
playerInfo[(landlordIdx + 3) % 4].douzeroPlayerPosition = 3;
|
||||
initHands[landlordIdx] = initHands[landlordIdx].concat(threeLandlordCards.slice());
|
||||
setGameStatus('playing');
|
||||
syncGameStatus = 'playing';
|
||||
|
@ -556,6 +608,12 @@ function PvEDoudizhuDemoView() {
|
|||
total: 0,
|
||||
winRate: '-',
|
||||
},
|
||||
{
|
||||
role: t('doudizhu.landlord_front'),
|
||||
win: 0,
|
||||
total: 0,
|
||||
winRate: '-',
|
||||
},
|
||||
{
|
||||
role: t('doudizhu.landlord_down'),
|
||||
win: 0,
|
||||
|
@ -575,25 +633,28 @@ function PvEDoudizhuDemoView() {
|
|||
// reset all game state for new game
|
||||
shuffledDoudizhuDeck = shuffleArray(fullDoudizhuDeck.slice());
|
||||
|
||||
threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 3)));
|
||||
threeLandlordCards = shuffleArray(sortDoudizhuCards(shuffledDoudizhuDeck.slice(0, 8)));
|
||||
originalThreeLandlordCards = threeLandlordCards.slice();
|
||||
|
||||
initHands = [
|
||||
shuffledDoudizhuDeck.slice(3, 20),
|
||||
shuffledDoudizhuDeck.slice(20, 37),
|
||||
shuffledDoudizhuDeck.slice(37, 54),
|
||||
shuffledDoudizhuDeck.slice(8, 33),
|
||||
shuffledDoudizhuDeck.slice(33, 58),
|
||||
shuffledDoudizhuDeck.slice(58, 83),
|
||||
shuffledDoudizhuDeck.slice(83, 108),
|
||||
];
|
||||
|
||||
playerInfo = [];
|
||||
|
||||
gameStateTimeout = null;
|
||||
gameHistory = [];
|
||||
bombNum = 0;
|
||||
bombNum = [0, 0, 0];
|
||||
lastMoveLandlord = [];
|
||||
lastMoveLandlordDown = [];
|
||||
lastMoveLandlordFront = [];
|
||||
lastMoveLandlordUp = [];
|
||||
playedCardsLandlord = [];
|
||||
playedCardsLandlordDown = [];
|
||||
playedCardsLandlordFront = [];
|
||||
playedCardsLandlordUp = [];
|
||||
legalActions = { turn: -1, actions: [] };
|
||||
|
||||
|
@ -602,8 +663,8 @@ function PvEDoudizhuDemoView() {
|
|||
setIsPassDisabled(true);
|
||||
setIsHintDisabled(true);
|
||||
setGameState({
|
||||
hands: [[], [], []],
|
||||
latestAction: [[], [], []],
|
||||
hands: [[], [], [], []],
|
||||
latestAction: [[], [], [], []],
|
||||
currentPlayer: null, // index of current player
|
||||
turn: 0,
|
||||
});
|
||||
|
@ -961,47 +1022,12 @@ function PvEDoudizhuDemoView() {
|
|||
</Layout.Col>
|
||||
<Layout.Col span="7" style={{ height: '100%' }}>
|
||||
<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' }}>
|
||||
{playerInfo.length > 0 && gameState.currentPlayer !== null ? (
|
||||
<span>
|
||||
{t(
|
||||
`doudizhu.${
|
||||
['landlord', 'landlord_down', 'landlord_up'][
|
||||
['landlord', 'landlord_down', 'landlord_front', 'landlord_up'][
|
||||
playerInfo[gameState.currentPlayer].douzeroPlayerPosition
|
||||
]
|
||||
}`,
|
||||
|
@ -1016,6 +1042,7 @@ function PvEDoudizhuDemoView() {
|
|||
<div className={'probability-item'}>{computeProbabilityItem(0)}</div>
|
||||
<div className={'probability-item'}>{computeProbabilityItem(1)}</div>
|
||||
<div className={'probability-item'}>{computeProbabilityItem(2)}</div>
|
||||
<div className={'probability-item'}>{computeProbabilityItem(3)}</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</Layout.Col>
|
||||
|
|
Loading…
Reference in New Issue