大富豪
ローカルルール
一番網羅されている感がある
英語
ルールをプログラマブルにしたい。大富豪ルール記述言語を作りたい
ペア出し
階段
革命
カード効果
切り
8切り
砂嵐
渡し
7渡し
捨て
10捨て
カード交換
ジョーカー
捨札操作
サタン
場のカード操作
税収
ゲーム強制終了
4が4枚
チョンボ
戦略的チョンボ
下切り
パスしたあとに回ってきたとき出せるか
分類
カードを出せるかどうか : Game -> Player -> [Card] -> Bool
強さ (革命状態か通常状態か)
縛り
階段
下切り・ダウンナンバー
砂嵐
パス済み
出したときにどのような効果が出るか : Game -> Player -> [Card] -> ()
{数字} が {N} 枚でたら、{何} をする
できなかった場合、{何} をする
{何}
革命系
カードの強さが変わる
その場の限り
ゲーム中ずっと
切り系
場が流れる
8切りに対して4止めとかもあるらしいので、即切れるんじゃなく切モードになって通常時と変わるみたいなほうがいいかもしれない?
切モードというか、8を出したらそれに強さが変わって4しか出せなくなるみたいな方がいいかも
これでも書けるけど8の効果になってしまいそう
あと強さを書くの面倒そう
切モードかつ場に8が出ているときは4を出せる、かなあ
これなら4の効果にできる
でもカードを出せるか判定にいちいち切モードかどうかを書かないといけなくて面倒
順番操作系
飛ばし
飛ばしたとき、一周以上したら切れるルールと切れないルールがある
飛ばしをどう扱うか。パス扱いにするかパスしていない扱いにするか
パスとスキップがありそう。パス済みで出せなくなるルールが影響する
逆順
その場だけあるいはゲーム中
手札操作系
手札を誰かにわたす
手札を誰かから奪う
手札を捨てる
数字を指定して捨てさせる
捨札操作系
捨札から拾う・交換する
場操作系
場のカードをもらう
カード効果が完全に発動できなかったとき : Player -> ()
何もしない
その時点で大貧民になる
ジョーカーの扱い
いろいろある
ひとつは、
ジョーカーのみの組の場合はどのカードにもならない最強のカードとして扱われる
ジョーカー1枚ならスペ3と砂嵐のみ、ジョーカー2枚なら砂嵐のみ出せる
ジョーカー単体を例えば8として使うのはなし
下切りができてスペ3の意味がなくなるから
ジョーカーと他のカードを同時に出す場合は、なにか一緒に出せるカードに化ける
スートも指定する必要がある
だけれど、いろいろありそう。これも書けるようにしたいがどうするか
流れ
1. 自分の手番になる
場に出ているトップの組が自分が出したカードであれば、流れる
2. 自分の手札のすべての組み合わせに対して場に出せるか判定する関数を適用して出せる組を探す (効率悪そうすぎるが)
3. パス or 出す
パスなら5へ
ジョーカーが含まれている場合はどのカードになるか選択する
4. カードを出したら効果を発動させる
ゲームに設定されている効果関数を、優先順位の順ですべて適用させる
5. 次の手番の人に回る
code:daifugo
# 11バック
if cards.contains(11) {
game.field.kakumei()
}
# 革命
if cards.uniq_numbers() == 1 && cards.size() >= 4 {
game.kakumei()
}
# 階段革命
if cards.uniq_suits() == 1 && cards.size() >= 4 {
game.kakumei()
}
# 7渡し
if cards.contains(7) {
# pick(枚数) で、自分の手札から好きなカードを枚数分選ぶ
# ここで失敗したらチョンボ処理へ
picked = player.pick(cards.size(7))
player.next().add(picked)
}
# Qシャッフル
if cards.contains(12) {
# parallel で並列にやりつつ終わるのを待つ
# この処理を挟むことでチョンボ処理を行う
game.players.parallel(p => {
# プレイヤーごとの一時保存領域に保存
p.store(p.pick(cards.size(12)))
})
game.players.parallel(p => {
# プレイヤーごとの一時保存領域からロード
# この時点でチョンボ処理が行われているはずなので、next() は終わっていないプレイヤーになっているはず
# これだと同時に死んだ場合どっちが下になるか決めないといけない気がしてきた
# 基本は順番に死ぬ気がする
p.next().add(p.load())
}
}
# 融合革命
# カードを出せるか
prev.size() + cards.size() == 4 && prev.uniq_numbers() == cards.uniq_numbers() && prev.uniq_numbers().size() == 1
# 効果
if (上記の式) {
# 融合革命って出したときに4枚にマージされるのかされないのかどっちだろう
# されるなら次は4枚出ししないといけないし、されないなら2-2で出したら次は2枚になる
game.kakumei()
}
# スート縛り (最初に出した組とその次に出した組のスートが完全に同じ場合だけ)
if game.field.stack.length() == 1 && game.field.stack0.uniq_suits() == cards.uniq_suits() { game.field.tighten(type=suit_only)
}
# 効果発動順どうするか
# カード効果の順序付けは全順序になるのか。そして機械的に判定できるか
# もう選ばせるでもいい?
# priority みたいなメタデータを別途つけておくでもいいかも。同じのときはランダム or ID順
# priority をつけるなら、
# - 革命は優先して発動させたい
# - 革命すると priority も反対になりたい、かも?革命すると、枚数ベースの優先順位はそのままで強さベースの優先順位だけ反対になってほしい感はある
# - 革命で反対になる優先順位と革命で反対にならない優先順位がある?
# - もうルールセットを作るときに優先順位を決めるでいい気がした。セットによって優先順位変えたいこともあるだろうし
code:daifugo
# 特殊変数
game : Game - そのゲームの状態
game.field : Field - 場
game.field.stack : Card - 場に出ているカードのスタック。stack0 でトップに出ている組を表す game.field.revolution : Normal | Revolution - 場が革命中かどうか
game.field.tight :
game.players : Player - まだ上がっていないプレイヤー player : Player - カードを出したプレイヤー
この言語でbotを作ることもできるかも。
アプリ
スクリプトはサーバで実行したくない
スクリプトはRaftかなんかで選ばれたリーダにやってもらうことにする
ゲームの状態はRaftで合意を取りながらやる
P2Pで実行するのは厳しそうなんでサーバが介入はすることにする
メッセージとログを受け渡すだけ