2ちゃんねる ■掲示板に戻る■ 全部 1- 最新50    

■ このスレッドは過去ログ倉庫に格納されています

暇だから麻雀のプログラムを考える

1 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
はい

2 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
30分だけね

3 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
課金したらツモが良くなる実装にしてくれ

4 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
プログラム?麻雀ゲームつくるってこと?

5 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
いまから考えるのは四槓散了と四槓子を考慮したツモです

6 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
>>3
そんな面倒な事はしません

7 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
>>4


8 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
手牌のスコアリングってカードゲーム全般で使い回し効きそう

9 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
言語はRustで
Yamaクラスを作ります
槓をしたプレイヤーが1人なのか複数なのかを見分けるためにプレイヤーのid(0~4)を保持します

pub struct Yama {
 last_kan_id: Option<u8>,
 …
}

impl Yama {
 pub fn kan(&mut self){}
}

10 :以下、?ちゃんねるからVIPがお送りします:[ここ壊れてます] .net
>>8
麻雀の得点は複雑すぎてどうだろう…

11 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:41:34.408 ID:lVxD3U/BM.net
last_kan_idには最初Noneを入れておきます
kan()メソッドではカンして嶺上牌をツモる操作を行います
ドラをめくる操作は別のメソッドに分離します

pub enum KanError {
 FiveTimesKanError,
 SukanError,
}

/// 戻り値は嶺上牌
pub fn kan(&mut self, player_id: u8) -> Result<PaiCode, KanError>;

12 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:44:05.656 ID:lVxD3U/BM.net
Yamaクラスにはkan_countというプライベートメンバを持たせます
yamaキューにシャッフルした136枚の牌を入れておき、末尾から引く場合普通のツモを、先頭から引く場合嶺上ツモを表すものとします

13 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:49:14.786 ID:lVxD3U/BM.net
ここまでのまとめ

pub struct Yama {
 yama: Vec<PaiCode>,
 kan_count: usize,
 last_kan_id: Option<u8>,
 …
}

impl Yama {
 pub fn new() -> Self {
   // yamaに0~135をシャッフルして入れる等
   todo!()
 }

 pub kan(&mut self, player_id: u8) -> Result<PaiCode, KanError> {
   // スマホで打つの辛すぎ
   todo!()
 }
}

14 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:53:07.990 ID:G3pmDGZ20.net
乱数でやれば良いだけか
将棋と違って麻雀って楽勝なんだな
底が浅いゲームだ

15 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:54:14.485 ID:RkwKot7u0.net
そんなんよりAI雀士をつくれ

16 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:54:45.292 ID:lVxD3U/BM.net
last_kan_idはベクタにした方がいいな…
変更
last_kan_id: Vec<u8>
このベクタのキャパシティは2とします

17 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:54:51.383 ID:CjeHcLxz0.net
>>14
余裕なさすぎだろ

18 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:55:38.208 ID:lVxD3U/BM.net
>>14
将棋は簡単なものは作ったが、プログラム書くのは正直麻雀の方がむずい

19 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:56:39.296 ID:lVxD3U/BM.net
>>15
シャンテン数が上がるように打つだけならまぁ…
対人戦が基本だからあんまり強いAIを作る事は考えてない

20 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:57:00.122 ID:lVxD3U/BM.net
30分経ってしまった
また時間があれば戻ってきます

21 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:57:06.830 ID:CjeHcLxz0.net
容量決まってるならスライスで良さげ

22 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 08:58:44.577 ID:RkwKot7u0.net
>>19
シャンテン上がるように切るだけならただの確率計算でしかないでしょ
作るのはスーパーフェニックスだよ

23 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:00:44.358 ID:HyQp2YnJ0.net
信じることが力さ

24 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:02:22.840 ID:MkdpUppu0.net
全ツしとけばVIPPERあいてならCPUが無敵だろ

25 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:07:14.553 ID:lVxD3U/BM.net
以下kanメソッドの中身

pub fn kan(&mut self, player_id: u8) -> Result<PaiCode, KanError> {
 if self.kan_count >= 4 {
   return Err(KanError::FiveTimesKanError);
 }

 let mut is_player_new = true;
 for &p in &self.kan_player_vec {
   if p == player_id {
     is_player_new = false;
     break;
   }
 }

 if is_player_new {
   self.kan_player_vec.push(player_id);
 }
 
 if self.kan_player_vec.len() >=2 && self.kan_count == 3 {
   return Err(KanError::


書きかけですが

26 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:14:52.710 ID:CjeHcLxz0.net
みてるよ

27 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:56:48.332 ID:lVxD3U/BM.net
pub fn kan(&mut self, player_id: u8) -> Result<PaiCode, KanError> {
 // 既に4回カンされていたら問答無用でエラー
 if self.kan_count >= 4 {
   return Err(KanError::FiveTimesKanError);
 }

 // 今回カンしたプレイヤーは初めてカンするのか?
 let mut is_player_new = true;
 for &p in &self.kan_player_vec {
   if p == player_id {
     is_player_new = false;
     break;
   }
 }

 // プレイヤーが初カンならば記録
 if is_player_new {
   self.kan_player_vec.push(player_id);
 }
 
 // 今回のカンを含め複数プレイヤーがカンしている
 // かつ既に3回カンされているなら四槓流れ
 if self.kan_player_vec.len() >= 2 && self.kan_count == 3 {
   return Err(KanError::SukanError);
 }

 // 嶺上牌をツモる
 let idx = (self.kan_count / 2) * 2 + (1 - self.kan_count % 2);
 self.kan_count += 1;

 Ok(self.yama[idx])
}

合ってるかな

28 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 09:57:12.590 ID:lVxD3U/BM.net
>>26
あいよ

29 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 10:28:28.584 ID:lVxD3U/BM.net
次に考えるべきシチュエーションは、プレイヤーにツモを送って暗槓された場合の対処

30 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 10:31:21.724 ID:lVxD3U/BM.net
暗槓された場合、暗槓に使われた牌の種類と新たなドラ表示牌を他のプレイヤーに送る必要がある

他のプレイヤーはそれらの情報を受け取って、暗槓を受理するか、または槍槓するか(国士のみ)を選んでサーバーに送る

槍槓が不可能なプレイヤーについては返答を待たず、槍槓が可能なプレイヤーについてのみ返答を待つことにする

31 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 10:32:07.216 ID:3bl0ZSQG0.net
スマホ打ちは辛すぎるだろ

32 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 10:32:50.374 ID:lVxD3U/BM.net
でもパソコン持ってきてないしな
長時間電車乗ってて暇だし別に構わん

33 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 10:51:29.967 ID:lVxD3U/BM.net
一局分を管理するクラスTableがあると想定する
Tableクラスはプレイヤーとやり取りをするためのplayerインスタンスを4つ保持しているとする
Tableクラスに以下のメソッドを持たせる

/// playerは暗槓をしたプレイヤーのインデックス
/// paiは暗槓時に晒す2つの牌の牌コード
/// 戻り値は、成功時は次にツモるプレイヤーのインデックス、失敗時は失敗理由(和了、流局等)
pub fn notify_ankan(&self, player: usize, pai: [PaiCode; 2]) -> Result<usize, Fin>;

34 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 11:28:00.943 ID:lVxD3U/BM.net
Playerクラスには以下のメソッドを持たせる

/// どのプレイヤーが暗槓をしたかは各プレイヤーは知っているはずなので引数では渡さない
/// 新たなドラ表示牌と暗槓時に晒される2牌を渡す
pub fn notify_ankan(&mut self, dora: PaiCode, pai: [PaiCode; 2]) -> AnkanResp;


AnkanRespは以下のように定義される

pub enum AnkanResp {
 Acc,
 Chankan(HoraInfo),
}

HoraInfoについては省略
役や手配、点数などを入れる

35 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 11:44:08.265 ID:lVxD3U/BM.net
Playerクラスの方の実装


pub async fn notify_ankan(&self, dora: PaiCode, pai: [PaiCode; 2]) -> AnkanResp {
 let packet = ServerPacket::Ankan { dora, pai };
 self.send(packet);
 if self.can_kokushi(pai[0]) {
   let resp = self.get_response::<ClientPacket>().await;
   match resp {
     ClientPacket::Acc => AnkanResp::Acc,
     ClientPacket::Ron => {
       // HoraInfoを構築する
       todo!()
       AnkanResp::Chankan(hora_info)
     },
     _ => AnkanResp::Acc,
   }
 } else {
   AnkanResp::Acc
 }
}

36 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 11:45:30.568 ID:HyQp2YnJ0.net


37 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 12:12:01.543 ID:aUzNqFi1M.net
ミスった
さっきkan()メソッドを書いたけどカン出来るかを判定するメソッドと実際にカンをするメソッドに分けるべきだった
can_kan()とkan()に分けます


以下Tableクラスの方の実装

pub async fn notify_ankan(&mut self, player: usize, pai: [PaiCode; 2]) -> Result<usize, Fin> {
 let rinshan_tsumo = self.yama.kan();
 let dora = self.yama.get_dora();

 let mut await_q = Vec::with_capacity(4);
 for p in self.players {
   await_q.push(p.notify_ankan(dora, pai));
 }

 let (a, b, c, d) = join!(
   await_q.pop().unwrap(), await_q.pop().unwrap(),
   await_q.pop().unwrap(), await_q.pop().unwrap(),
 );

 let mut hora_ed = Vec::with_capacity(4);
 for resp in &[a, b, c, d] {
   match resp {
     AnkanResp::Acc => {},
     AnkanResp::Chankan(hora_info) => {
       hora_ed.push(hora_info.clone());
     }
 }

 if !hora_ed.is_empty() {
   return Err(Fin::Ron(hora_ed));
 } else {
   let is_rinshan = true;
   return self.send_tsumo(player, rinshan_tsumo, is_rinshan);
 }
}

38 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 12:21:12.225 ID:1braDTVV0.net
とっくに30分過ぎてるけど…

39 :以下、?ちゃんねるからVIPがお送りします:2022/09/15(木) 12:23:30.213 ID:dUQG5oo30.net
乗り過ごしたか?

総レス数 39
12 KB
掲示板に戻る 全部 前100 次100 最新50
read.cgi ver.24052200