Permalink
Browse files

support bots

  • Loading branch information...
1 parent ae4bf0e commit b357db72b620edc1f3ebcbb5952dd1e3072e426e @winbomb committed Jul 18, 2012
Showing with 495 additions and 70 deletions.
  1. +8 −4 src/board.opa
  2. +329 −0 src/bot.opa
  3. +50 −16 src/game.opa
  4. +2 −1 src/login.opa
  5. +71 −30 src/mahjong.opa
  6. +5 −2 src/main.opa
  7. +17 −5 src/page.opa
  8. +11 −10 src/render.opa
  9. +2 −2 src/testutil.opa
View
12 src/board.opa
@@ -196,7 +196,7 @@ module Board {
//首先检查手牌中是否存在 2/5/8 的将
if(not(has_general(handcards))) {false} else {
//找到将
- generals = find_general(handcards);
+ generals = find_jong(handcards);
List.fold(function(general,can_hoo){
if(can_hoo) can_hoo else {
//cards为去掉两张将牌之后的牌
@@ -234,7 +234,7 @@ module Board {
}
/** 从一组牌中找出一对将牌 */
- function find_general(cards){
+ function find_jong(cards){
List.fold(function(general,result){
pair = List.fold(function(c,pair){
if(List.length(pair) == 2) pair else {
@@ -320,13 +320,13 @@ module Board {
},deck.handcards,gangs);
}
- private function filter_group(cards,group){
+ function filter_group(cards,group){
List.filter(function(c){
c.id != group.card1.id && c.id != group.card2.id && c.id != group.card3.id
},cards);
}
- private function filter_pair(cards,pair){
+ function filter_pair(cards,pair){
List.filter(function(c){
c.id != pair.card1.id && c.id != pair.card2.id
},cards);
@@ -654,6 +654,10 @@ module Card {
}
}
+ function is_jong(card){
+ card.point == 2 || card.point == 5 || card.point == 8
+ }
+
function to_string(card){
point = "{card.point}"
suit = match(card.suit){
View
329 src/bot.opa
@@ -0,0 +1,329 @@
+/*************************************************************************
+ * Mahjong: An html5 mahjong game built with opa.
+ * Copyright (C) 2012
+ * Author: winbomb
+ * Email: li.wenbo@whu.edu.cn
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ ************************************************************************/
+package mahjong
+
+server module Bot {
+
+ function call(game,idx){
+ match(LowLevelArray.get(game.players,idx)){
+ case {none}: Mahjong.request_action(game.id,idx,{no_act});
+ case ~{some}: {
+ card = Option.get(game.curr_card);
+ deck = LowLevelArray.get(game.board.decks,idx);
+ //如果能胡,胡之
+ if(Board.can_hoo_card(deck,card)) Mahjong.request_action(game.id,idx,{hoo}) else{
+ //如果能杠,则杠之
+ if(Board.can_gang(deck,card)) Mahjong.request_action(game.id,idx,{gang}) else {
+ //如果能碰,考虑之
+ if(Board.can_peng(deck,card)){
+ cards = List.sort_with(Card.compare,deck.handcards);
+ jong_cnt = jong_cnt(cards);
+ if(Card.is_jong(card) && jong_cnt <= 1){
+ Mahjong.request_action(game.id,idx,{no_act});
+ }else{
+ match(in_use(cards,card)){
+ case {true}: Mahjong.request_action(game.id,idx,{no_act});
+ case {false}: Mahjong.request_action(game.id,idx,{peng});
+ }
+ }
+ }else Mahjong.request_action(game.id,idx,{no_act});
+ }
+ }
+ }}
+ }
+
+ function play(game,idx){
+ deck = LowLevelArray.get(game.board.decks,idx);
+ //如果能胡,胡之
+ if(Board.can_hoo_self(deck)) Mahjong.request_action(game.id,idx,{hoo}) else {
+ //如果能杠,杠之
+ if(Board.can_gang_self(deck)) Mahjong.request_action(game.id,idx,{gang}) else{
+ //否则从handcards里面选一张牌弃掉
+ cards = List.sort_with(Card.compare,deck.handcards);
+ jong = select_jong(cards);
+ cards = match(jong){
+ case {none}: cards
+ case ~{some}: Board.filter_pair(cards,some)
+ }
+ groups = split(cards);
+
+ match(find_single(groups,jong)){
+ case ~{some}: Mahjong.request_action(game.id,idx,{discard:some});
+ case {none}:{
+ match(decide(groups,jong)){
+ case ~{some}: Mahjong.request_action(game.id,idx,{discard:some});
+ case {none}: {
+ jlog("WARN: random selection!");
+ idx = Random.int(List.length(cards));
+ Mahjong.request_action(game.id,idx,{discard: Option.get(List.get(idx,cards))});
+ }}
+ }}
+ }
+ }
+ }
+
+ function in_use(cards,card){
+ //TODO
+ {false}
+ }
+
+ function jong_cnt(cards){
+ List.foldi(function(i,c,cnt){
+ pp = get_point(cards,i-1);
+ np = get_point(cards,i+1);
+ if(i <= List.length(cards)-1 && np == c.point && pp != c.point && Card.is_jong(c)){
+ cnt + 1;
+ }else cnt
+ },cards,0);
+ }
+
+ //从组牌中随机选择一张
+ function rand_pick(cards){
+ n = List.length(cards);
+ if(n == 0) {none} else List.get(Random.int(n),cards);
+ }
+
+ function get_point(cards,idx){
+ if(idx <= -1) -99 else {
+ if(idx >= List.length(cards)) 99 else{
+ match(List.get(idx,cards)){
+ case {none}: 0
+ case ~{some}: some.point
+ }
+ }
+ }
+ }
+
+ function get_suit(cards,idx){
+ if(idx <= -1 || idx >= List.length(cards)) {none} else{
+ match(List.get(idx,cards)){
+ case {none}: {none}
+ case {some:c}: some(c.suit)
+ }
+ }
+ }
+
+ //
+ function find_shun(cards){
+ n = List.length(cards);
+ List.foldi(function(i,c,result){
+ if(result != {none}) result else {
+ nnp = get_point(cards,i+2); //next next point
+ np = get_point(cards,i+1); //next point
+ cp = c.point; //current point
+ if(i <= n-2 && np == cp+1 && nnp == cp+2){
+ shun = {card1:c,card2:Option.get(List.get(i+1,cards)),card3:Option.get(List.get(i+2,cards))}
+ some(shun)
+ }else result
+ }
+ },cards,{none});
+ }
+
+ function find_ke(cards){
+ n = List.length(cards);
+ List.foldi(function(i,c,result){
+ if(result != {none}) result else {
+ nnp = get_point(cards,i+2); //next next point
+ np = get_point(cards,i+1); //next point
+ cp = c.point; //current point
+ if(i <= n-2 && np == cp && nnp == cp){
+ ke = {card1:c,card2:Option.get(List.get(i+1,cards)),card3:Option.get(List.get(i+2,cards))}
+ some(ke)
+ }else result
+ }
+ },cards,{none});
+ }
+
+ function find_jong(cards){
+ n = List.length(cards);
+ List.foldi(function(i,c,jong){
+ pp = get_point(cards,i-1);
+ np = get_point(cards,i+1);
+ if(i <= n-1 && np == c.point && pp != c.point && Card.is_jong(c)){
+ some({card1:c,card2:Option.get(List.get(i+1,cards))});
+ }else jong
+ },cards,{none});
+ }
+
+ function select_jong(cards){
+ groups = split(cards);
+ result = List.fold(function(g,result){
+ if( List.length(g) <= 1 || (result.jong != {none} && result.left == 0)) result else {
+ match(find_jong(g)){
+ case {none}: result
+ case {some:jong}: {
+ if(List.length(g) == 2) {jong:some(jong),left:0} else {
+ left = find(Board.filter_pair(g,jong)); //
+ if(result.jong == {none} || result.left > List.length(left)){
+ {jong:some(jong),left:List.length(left)}
+ }else result
+ }
+ }}
+ }
+ },groups,{jong:{none},left:0});
+
+ result.jong;
+ }
+
+ function find(cards){
+ if(List.length(cards) <= 2) cards else {
+ best1 = match(find_shun(cards)){
+ case {none}: cards
+ case ~{some}: find(Board.filter_group(cards,some))
+ }
+
+ best2 = match(find_ke(cards)){
+ case {none}: cards
+ case ~{some}: find(Board.filter_group(cards,some))
+ }
+
+ if(List.length(best1) <= List.length(best2)) best1 else best2
+ }
+ }
+
+ function choose(cards,jong){
+ if(List.length(cards) == 0) {none} else {
+ match(jong){
+ case {some:_}: rand_pick(cards);
+ case {none}:{
+ n = List.length(cards);
+ List.foldi(function(i,c,card){
+ if(card != {none}) card else{
+ if(not(Card.is_jong(c)) || (i == n-1 && card == {none})) some(c) else card
+ }
+ },cards,{none});
+ }}
+ }
+ }
+
+ function decide(groups,jong){
+ tg1 = List.filter(function(g){mod(List.length(g)-1,3) == 0},groups); //4,7,10,13
+ cands1 = List.fold(function(g,c){
+ List.append(find(g),c);
+ },tg1,[]);
+
+ if(List.length(cands1) >= 1) choose(cands1,jong) else {
+ tg2 = List.filter(function(g){mod(List.length(g),3) == 0},groups); //3,6,9,12
+ cands2 = List.fold(function(g,c){
+ left = find(g);
+ if(List.length(left) == 0) c else List.append(left,c);
+ },tg2,[]);
+
+ if(List.length(cands2) >= 1) choose(cands2,jong) else {
+ tg3 = List.filter(function(g){mod(List.length(g)+1,3) == 0},groups);
+ cands3 = List.fold(function(g,c){
+ List.append(find(g),c);
+ },tg3,[]);
+ choose(cands3,jong);
+ }
+ }
+ }
+
+ function split(cards){
+ result = List.foldi(function(i,c,result){
+ same_suit = match(get_suit(cards,i+1)){
+ case {none}: {false}
+ case ~{some}: some == c.suit
+ }
+ if(i >= List.length(cards)-1 || get_point(cards,i+1) - c.point > 1 || not(same_suit)){
+ group = List.filteri(function(j,c){j >= result.last_idx && j <= i},cards);
+ {result with groups: group +> result.groups, last_idx: i+1}
+ }else result
+ },cards,{groups:[],last_idx:0});
+ result.groups;
+ }
+
+ //寻找孤立的牌(与左右其他牌点数相差2以上)
+ function find_single(groups,jong){
+ cands = List.fold(function(g,s){
+ if(List.length(g) != 1) s else List.head(g) +> s
+ },groups,[]);
+
+ if(List.length(cands) == 0) {none} else choose(cands,jong)
+ }
+}
+
+module TestBot {
+ suits = {
+ //test_find_single();
+ //test_split();
+ //test_decide();
+ //test_find();
+ //test_select_jong();
+ }
+
+ function test_find_single(){
+
+ HANDS = ["3W","3W","4W","5W","7W","4B","5B","6B","1T","2T","4T","5T","8T","8T"];
+ deck = List.fold(function(p,c){ TestUtil.trans_card(p) +> c },HANDS, []);
+ cards = List.sort_with(Card.compare,deck);
+ groups = Bot.split(cards);
+
+ result = Bot.find_single(groups,{none});
+ jlog("result = {result}");
+ }
+
+ function test_split(){
+ jlog("test split tiles");
+ TILES = ["1W","2W","4W","5W","6W","8W","9W","4B","5B","6B","6B","7T","8T"];
+ cards = List.fold(function(p,c){ TestUtil.trans_card(p) +> c },TILES, []);
+ cards = List.sort_with(Card.compare,cards);
+
+ result = Bot.split(cards);
+ jlog("result count = {List.length(result)}");
+ List.iter(function(part){
+ jlog("Part: {part}");
+ },result);
+ }
+
+ function test_decide(){
+ jlog("test decide");
+ GROUPS = [["2W","2W","3W","4W","5W","6W","7W","8W","9W"],["2T","3T","4T","4T","5T"]];
+ input = List.fold(function(g,input){
+ cards = List.fold(function(p,c){ TestUtil.trans_card(p) +> c },g, []);
+ cards = List.sort_with(Card.compare,cards);
+ cards +> input
+ },GROUPS,[]);
+
+ result = Bot.decide(input,{none});
+ jlog("result = {result}");
+ }
+
+ function test_find(){
+ jlog("test find");
+ TILES = ["2W","2W","3W","4W","5W","6W","7W"];
+ cards = List.fold(function(p,c){ TestUtil.trans_card(p) +> c },TILES, []);
+ cards = List.sort_with(Card.compare,cards);
+
+ result = Bot.find(cards);
+ jlog("result = {result}");
+ }
+
+ function test_select_jong(){
+ jlog("test select jong");
+ TILES = ["5B","6B","7B","2T","2T","3T","4T","2W","3W","5W","5W","6W","8W","8W"];
+ cards = List.fold(function(p,c){ TestUtil.trans_card(p) +> c },TILES, []);
+ cards = List.sort_with(Card.compare,cards);
+
+ result = Bot.select_jong(cards);
+ jlog("result = {result}");
+ }
+}
View
66 src/game.opa
@@ -217,6 +217,42 @@ module Game {
}
}
+ // 向游戏中添加机器人
+ function add_bots(game){
+ players = LowLevelArray.mapi(game.players)(function(i,player){
+ match(player){
+ case {none}:{
+ name = "Bot {Random.int(2000)}";
+ some({~name,idx:i,is_bot:{true},status:{online},coins:DEFAULT_COINS});
+ }
+ case {some:player}: some(player);
+ }
+ })
+
+ ready_flags = LowLevelArray.foldi(function(i,player,result){
+ match(player){
+ case {none} : set_flag(result,i);
+ case ~{some}: if(some.is_bot) set_flag(result,i) else result;
+ }
+ },players,0);
+
+ {game with ~players,~ready_flags}
+ }
+
+ /** 获得可以加入的游戏的id(未开始,人数为0) */
+ exposed function get_empty_gameid(){
+ game_opt = Map.find(function(_,game){
+ if(game.status != {prepare} && game.status != {game_over}) {false} else {
+ if(get_player_cnt(game.players) >= 1) {false} else {true}
+ }
+ },ServerReference.get(gmMap));
+
+ match(game_opt){
+ case {none}: {none}
+ case {some:s}: some(s.val.id)
+ }
+ }
+
/** 获取游戏信息列表 */
public exposed function get_game_list(){
Map.fold(function(_,game,result){
@@ -239,7 +275,7 @@ module Game {
LowLevelArray.fold(function(player,count){
match(player){
case {none}: count;
- case ~{some}: if(some.status == {online}) count+1 else count
+ case ~{some}: if(some.is_bot == {false} && some.status == {online}) count+1 else count
}
},players,0);
}
@@ -645,11 +681,6 @@ module Game {
</div>
</>
);
- /** Resource.styled_page("Mahjong",["/resources/style.css"],
- <>
- <h1>Hello Mahjong!</h1>
- </>
- );*/
}
@async function post_chat_msg(author,channel){
@@ -691,15 +722,16 @@ module Game {
/** 开始游戏 */
function start(game){
- {game with
+ game = {game with
board: Board.prepare(game.board),
status: {select_action},
curr_turn: 0,
change_flag: {true},
ready_flags: 0,
ok_flags: 0,
- actions: Mahjong.reset_actions(game)
- }
+ }
+
+ Mahjong.reset_actions(game);
}
/** 重新开始游戏
@@ -719,14 +751,16 @@ module Game {
case {some:p}: if(p.status == {offline}) {none} else some(p)
}
});
+
+ //把所有机器人的Ready状态设置为ready
+ ready_flags = LowLevelArray.foldi(function(i,player,result){
+ match(player){
+ case {none} : result
+ case ~{some}: if(some.is_bot) set_flag(result,i) else result;
+ }
+ },players,0);
- {game with
- players: players,
- ok_flags: 0,
- curr_turn: 0,
- change_flag: {true},
- actions: Mahjong.reset_actions(game)
- }
+ {game with ~players, ~ready_flags, ok_flags:0, curr_turn:0, change_flag:{true}} |> Mahjong.reset_actions(_);
}
/** 获取某个玩家的deck */
View
3 src/login.opa
@@ -28,6 +28,7 @@ type Player.t = {
string name, //用户名
int idx, //坐标(0-3)
// bool is_ready, //是否准备好
+ bool is_bot, //是否是机器人
Player.status status, //链路状态
int coins, //积分
}
@@ -86,7 +87,7 @@ module Login {
function attempt_login(){
name = Dom.get_value(#username);
if(not(String.is_empty(name))){
- user = {~name, idx: -1, status: {online}, coins: DEFAULT_COINS};
+ user = {~name, idx: -1, is_bot: {false}, status: {online}, coins: DEFAULT_COINS};
set_cookie("login_name",name);
UserContext.change(function(_){{user:user}},state);
View
101 src/mahjong.opa
@@ -205,21 +205,30 @@ module Mahjong {
/**
* 弃牌
*/
- private function discard(game,idx,card){
+ public function discard(game,idx,card){
deck = LowLevelArray.get(game.board.decks,idx);
card_cnt = List.length(deck.handcards) + List.length(deck.donecards)*3
if(card_cnt <= 13){
Log.warning("Mahjong","Try to discard card, but only has 13 cards.");
}else{
- game = {game with board: Board.discard(game.board,idx,card)}
- game = {game with status: {wait_for_resp}}
- game = {game with curr_card: some(card)}
- game = {game with actions: reset_actions(game)} |> update(_)
+ game = {game with board: Board.discard(game.board,idx,card),
+ status:{wait_for_resp},
+ curr_card: some(card)} |> reset_actions(_) |> update(_);
Network.broadcast({DISCARD_CARD: {index:idx,~card} },game.game_channel)
//如果没有人能够动作,则直接进入下一个玩家
b_done = LowLevelArray.for_all(game.actions)(function(act){act == {no_act}})
- if(b_done) Scheduler.sleep(2000,function(){next_turn(game.id)});
+ if(b_done) Scheduler.sleep(2000,function(){next_turn(game.id)}) else{
+ Scheduler.sleep(1500,function(){
+ LowLevelArray.iteri(function(i,act){
+ match(LowLevelArray.get(game.players,i)){
+ case {none}: void
+ case ~{some}:{
+ if(some.is_bot && act == {none}) Bot.call(game,i);
+ }}
+ },game.actions);
+ });
+ }
}
}
@@ -228,24 +237,34 @@ module Mahjong {
* 玩家player碰
*/
private function peng(game,idx){
- game = {game with board: Board.peng(game,idx,Option.get(game.curr_card))}
- game = {game with status: {select_action}}
- game = {game with curr_turn: idx}
- game = {game with curr_card: {none}} |> update(_)
-
+ game = {game with board: Board.peng(game,idx,Option.get(game.curr_card)),
+ status: {select_action},
+ curr_turn: idx,
+ curr_card: {none}} |> update(_);
+
Network.broadcast({NEXT_ACTION: Game.game_msg(game),ACT:{peng}},game.game_channel)
+
+ match(LowLevelArray.get(game.players,idx)){
+ case {none}: void
+ case ~{some}: if(some.is_bot) Scheduler.sleep(1000,function(){Bot.play(game,idx)})
+ }
}
/**
* 玩家player杠牌
*/
private function gang_card(game,idx){
- game = {game with board: Board.gang(game,idx,Option.get(game.curr_card))}
- game = {game with status: {select_action}}
- game = {game with curr_turn: idx}
- game = {game with curr_card: {none}} |> update(_)
+ game = {game with board: Board.gang(game,idx,Option.get(game.curr_card)),
+ status: {select_action},
+ curr_turn: idx,
+ curr_card: {none}} |> update(_)
Network.broadcast({NEXT_ACTION: Game.game_msg(game),ACT:{gang}},game.game_channel)
+
+ match(LowLevelArray.get(game.players,idx)){
+ case {none}: void
+ case ~{some}: if(some.is_bot) Scheduler.sleep(1000,function(){Bot.play(game,idx)})
+ }
}
/**
@@ -284,12 +303,17 @@ module Mahjong {
//更新deck
LowLevelArray.set(game.board.decks,idx,deck)
- game = {game with board: Board.deal_card(game.board,idx)}
- game = {game with status: {select_action}}
- game = {game with curr_turn: idx}
- game = {game with curr_card: {none}} |> update(_)
+ game = {game with board: Board.deal_card(game.board,idx),
+ status: {select_action},
+ curr_turn: idx,
+ curr_card: {none}} |> update(_)
Network.broadcast({NEXT_ACTION: Game.game_msg(game),ACT:{gang_self}},game.game_channel)
+
+ match(LowLevelArray.get(game.players,idx)){
+ case {none}: void
+ case ~{some}: if(some.is_bot) Scheduler.sleep(1000,function(){Bot.play(game,idx)})
+ }
}
/**
@@ -321,10 +345,19 @@ module Mahjong {
private function show_result(game){
//计算玩家的本局的积分
result = calc_scores(game);
-
+
//更新玩家的积分
players = update_scores(game.players,result);
- game = {game with players: players,status:{show_result}} |> update(_)
+
+ //把所有机器人的OK状态设置为ready
+ ok_flags = LowLevelArray.foldi(function(i,player,result){
+ match(player){
+ case {none} : result
+ case ~{some}: if(some.is_bot) set_flag(result,i) else result;
+ }
+ },players,0);
+
+ game = {game with ~players, ~ok_flags, status:{show_result}} |> update(_)
//广播得分消息
Network.broadcast({SHOW_RESULT: some(result)},game.game_channel)
@@ -387,6 +420,12 @@ module Mahjong {
LowLevelArray.set(game.players,idx,{none});
game = update(game);
Network.broadcast({PLAYER_CHANGE: Game.game_msg(game)},game.game_channel)
+
+ //如果游戏的player都离开了,则结束游戏。
+ if(Game.get_online_cnt(game.players) == 0){
+ game = {game with ready_flags:0,players: LowLevelArray.create(4,{none})}; //清空玩家
+ restart(game);
+ }
} else {
players = LowLevelArray.mapi(game.players)(function(i,p){
if(i != idx) p else {
@@ -397,9 +436,10 @@ module Mahjong {
}
});
game = {game with players: players} |> update(_);
-
+
//如果游戏的player都离开了,则结束游戏。
if(Game.get_online_cnt(game.players) == 0){
+ game = {game with ready_flags:0,players: LowLevelArray.create(4,{none})}; //清空玩家
restart(game);
}
}
@@ -597,8 +637,10 @@ module Mahjong {
match(LowLevelArray.get(game.players,game.curr_turn)){
case {none}: Scheduler.sleep(1000,function(){ default_action(game)});
case {some:player}:{
- if(player.status != {online}) {
+ if(player.is_bot == {false} && player.status != {online}) {
Scheduler.sleep(1000,function(){ default_action(game)});
+ }else{
+ if(player.is_bot) Scheduler.sleep(1000,function(){Bot.play(game,game.curr_turn)});
}
}
}
@@ -607,26 +649,25 @@ module Mahjong {
}
function reset_actions(game){
- idx = game.curr_turn
- LowLevelArray.mapi(game.actions)(function(i,_){
- //如果第i个玩家可以碰/杠/胡,则把action设置为{none},否则设置为{no_act}
+ actions = LowLevelArray.init(4)(function(i){
deck = LowLevelArray.get(game.board.decks,i);
can_act = match(game.curr_card){
- case {none}: {false}
+ case {none}: {false}
case ~{some}: {
Board.can_peng(deck,some) || Board.can_gang(deck,some) || Board.can_hoo_card(deck,some);
}
}
match(LowLevelArray.get(game.players,i)){
- case {none}: {no_act}
+ case {none}: {no_act}
case ~{some}: {
if(some.status != {online}) {no_act} else {
- if(i != idx && can_act) {none} else {no_act}
+ if(i != game.curr_turn && can_act) {none} else {no_act}
}
}
- }
+ }
});
+ {game with ~actions};
}
/**
View
7 src/main.opa
@@ -42,7 +42,7 @@ function with_user((Login.user -> 'a) f, 'a otherwise){
}
}
-function game_view(game_id){
+function game_view(game_id,need_bot){
match(Login.get_user()){
case {unlogged}: Login.login_view();
case {user:player}: {
@@ -58,7 +58,9 @@ function game_view(game_id){
player = {player with status: {online}}
LowLevelArray.set(game.players,idx,some(player))
LowLevelArray.set(game.clients,idx,some(c));
+ game = if(need_bot) Game.add_bots(game) else game;
game = {game with change_flag:{true}} |> Game.update(_)
+
Game.game_view(game,idx);
}
default: Page.game_list_view()
@@ -73,7 +75,8 @@ function start(url){
match(url) {
case {path:[] ... } : Login.login_view()
case {path:["login"] ... } : Login.login_view()
- case {path:["game",x|_] ...} : game_view(x);
+ case {path:["game",id|_] ...} : game_view(id,{false});
+ case {path:["gamex",id|_] ...} : game_view(id,{true});
case {path:["how_to_play.html"] ...}: @static_resource("resources/how_to_play.html");
case {path:["hall"] ...} : login_required(function(){Page.game_list_view()})
case {path: _ ...} : Main.fourOffour()
View
22 src/page.opa
@@ -70,24 +70,36 @@ module Page {
},message)
}
- client function join_game(){
- match(Game.get_free_gameid()){
+ client function join_game(need_bot){
+ match(need_bot){
+ case {false}:{
+ match(Game.get_free_gameid()){
case {none}: jlog("unable to join game.");
case {some:id}: {
Client.goto("/game/{id}");
- }
+ }}
}
+ case {true}:{
+ match(Game.get_empty_gameid()){
+ case {none}: jlog("unable to join game.");
+ case {some:id}: {
+ Client.goto("/gamex/{id}");
+ }}
+ }}
}
function game_list_view(){
Resource.styled_page("Welcome",["/resources/page.css"],
<>
<div class="dragon_bg"></div>
<div id="game_list" onready={function(_){ page_ready()}} >
- <div class="title"><h2>Game List</h2></div>
+ <div class="title"><h2>Welcome xxxx </h2></div>
<div class="quick-start">
+ <input type="button" class="btn btn-primary" value="Play With Bots"
+ onclick={function(_){join_game({true})}}/>
<input type="button" class="btn btn-primary" value="Quick Start"
- onclick={function(_){join_game()}}/>
+ onclick={function(_){join_game({false})}}/>
+ <input type="button" class="btn btn-primary" value="Leave"/>
</div>
<div class="game_list_pannel">
<table class="tb_game_list">
View
21 src/render.opa
@@ -55,7 +55,8 @@ get_game = %%engine2d.get_game%%;
* 游戏的绘制引擎
*/
client module Render {
- bool DEBUG = {false}
+ bool DEBUG = {true}
+ bool SHOW_TILE = {true}
//常用颜色的定义
BLACK = {color:Color.rgb(0,0,0)};
@@ -467,10 +468,10 @@ client module Render {
},deck.donecards);
start_y = 85 + 82*List.length(deck.donecards);
- if((game.status != {game_over} && game.status != {show_result}) || deck.handcards == {nil}){
- ignore(for(0,function(n){ draw_covered(ctx,rel_pos,120,start_y+22*n); n+1;}, _ <= card_cnt - 1))
- }else{
+ if(SHOW_TILE || game.status == {game_over} || game.status == {show_result}){
List.iteri(function(n,c){ draw_down_card_left(ctx,c,100,start_y+22*n,{false}); },deck.handcards);
+ }else{
+ ignore(for(0,function(n){ draw_covered(ctx,rel_pos,120,start_y+22*n); n+1;}, _ <= card_cnt - 1))
}
}
case 2:{
@@ -492,10 +493,10 @@ client module Render {
//绘制手牌
start_x = 545 - 85*List.length(deck.donecards) - 25*card_cnt;
- if((game.status != {game_over} && game.status != {show_result}) || deck.handcards == {nil}){
- ignore(for(0,function(n){ draw_covered(ctx,2,start_x+25*n,5); n+1;}, _ <= card_cnt-1))
- }else{
+ if(SHOW_TILE || game.status == {game_over} || game.status == {show_result}){
List.iteri(function(n,c){ draw_down_card_oppt(ctx,c,start_x+25*n,5,{false}); },deck.handcards);
+ }else{
+ ignore(for(0,function(n){ draw_covered(ctx,2,start_x+25*n,5); n+1;}, _ <= card_cnt-1))
}
}
case 3:{
@@ -513,10 +514,10 @@ client module Render {
//绘制手牌
start_y = 465 - 82*List.length(deck.donecards) - 22*(card_cnt+1)
- if((game.status != {game_over} && game.status != {show_result}) || deck.handcards == {nil}){
- ignore(for(0,function(n){ draw_covered(ctx,3,600,start_y+22*n); n+1;}, _ <= card_cnt-1))
- }else{
+ if(SHOW_TILE || game.status == {game_over} || game.status == {show_result}){
List.iteri(function(n,c){ draw_down_card_right(ctx,c,600,start_y+22*n,{false}); },deck.handcards);
+ }else{
+ ignore(for(0,function(n){ draw_covered(ctx,3,600,start_y+22*n); n+1;}, _ <= card_cnt-1))
}
}
default: void
View
4 src/testutil.opa
@@ -22,13 +22,13 @@ package mahjong
server module TestUtil {
//四位玩家起到手的牌 (测试杠)
- HANDCARDS =[["1W","1W","1W","2W","2W","2W","3W","3W","3W","4W","4W","4W","5W","5W"],
+ HANDCARDS_BAK1 =[["1W","1W","1W","2W","2W","2W","3W","3W","3W","4W","4W","4W","5W","5W"],
["1B","1B","1B","1B","2B","2B","2B","2B","3B","3B","3B","3B","4B"],
["1T","1T","1T","1T","2T","2T","2T","2T","3T","3T","3T","3T","4T"],
["5W","8W","5W","5B","5B","5B","5T","5T","5T","6B","6B","6B","6B"]]
//测试一炮多响
- HANDCARDS_BAK2 = [["1W","1W","1W","2W","2W","2W","3W","3W","3W","4W","4W","4W","5W","5W"],
+ HANDCARDS = [["1W","1W","1W","2W","2W","2W","3W","3W","3W","4W","4W","4W","5W","5W"],
["1B","1B","1B","2B","2B","2B","3B","3B","3B","6W","7W","8B","8B"],
["1T","1T","1T","2T","2T","2T","3T","3T","3T","6W","7W","8T","8T"],
["1W","2W","3W","1B","2B","3B","1T","2T","3T","9B","9B","5W","5W"]]

0 comments on commit b357db7

Please sign in to comment.