環境:
macOS Catalina バージョン 10.15.1
Xcode Version 11.2.1
やりたい事:
Xcode の PlayGround で簡単な囲碁プログラムを作ろうとしています。
下記コードを実行すると
......... ......... ......... .....@... ...@.oo.. ......... ....o.... ......... .........
のように出力されます。
コードのコメント(1)~(5)のように対局が進む間は「着手」を「棋譜」にappendしていけばいいのですが、コメント(6)のように一旦手を戻して(7)のように別の着手をした場合に、現段階では(5)が消えてしまい、(1)(2)(3)(4)(7)と打ったように配列がなってしまいます。
これを、(1)~(5)はそれはそれで取って置き、それを消さずに(7)の変化図も記憶しておきたいのです。
つまり、
(1) -> (2) -> (3) -> (4) -> (5) L> (7)
のような状態を何らかの方法で保持したいのです。
こういった場合にどんなアルゴリズムが用いられるのかをご教授願いたいです。
よろしくお願いします。
swift
1let 黒: String = "@" 2let 白: String = "o" 3let 空: String = "." 4let 盤の大きさ: Int = 9 5var 手番: String = 黒 6 7struct 着手 { 8 var 色: String 9 var x: Int 10 var y: Int 11 init(色: String, x: Int, y: Int) { 12 self.色 = 色 13 self.x = x 14 self.y = y 15 } 16} 17 18var 棋譜: [着手] = [] 19var 局面: [[String]] = Array(repeating: Array(repeating: 空, count: 盤の大きさ), count: 盤の大きさ) 20 21func 着手する(x: Int, y: Int) { 22 棋譜.append(着手(色: 手番, x: x - 1, y: y - 1)) 23 手番 = 手番 == 黒 ? 白 : 黒 24} 25 26func 戻す() { 27 let 最後の着手: 着手 = 棋譜.popLast()! 28 局面[最後の着手.y][最後の着手.x] = 空 29 手番 = 手番 == 黒 ? 白 : 黒 30} 31 32着手する(x: 6, y: 4) // (1) 33着手する(x: 5, y: 7) // (2) 34着手する(x: 4, y: 5) // (3) 35着手する(x: 7, y: 5) // (4) 36着手する(x: 7, y: 4) // (5) 37戻す() // (6) 38着手する(x: 6, y: 5) // (7) 39 40for i in 0 ..< 棋譜.count { 41 局面[棋譜[i].y][棋譜[i].x] = 棋譜[i].色 42} 43for y in 0 ..< 盤の大きさ { 44 for x in 0 ..< 盤の大きさ { 45 print(局面[y][x], separator: " ", terminator: "") 46 } 47 print() 48}
追記、masatoUchidaさんの回答を元に考えてみたこと(2019.11.22 19:47)
なるほどと思いました。
で、「何手目か」と「分岐番号」を(何手目か, 分岐番号)で簡略的に表しながら考えてみました。
まずは初期状態から5手まで打ったのが下の0行目です。
ここで(2, 0)に戻って3手目で別の手を打ち5手目まで打ったのが1行目。
更に(1, 0)に戻って別の2手目を打って5手目まで打ったのが2行目。
で、ここからが本題です。
(3, 0)に戻って(4, 0)とは別の4手目を打った場合には(4, 3)と表すことになるのかなと思います。一旦そうします。
今度はに(3, 1)に戻って(4, 1)とは別の4手目を打った場合に(4, 4)と表すこととしたとします。
すると、一見良さそうに見えますが、この図だけから見ると(4, 4)はどこから派生したのかが分からなくなります。
つまり、(4, 4)は(3, 1)の次の手に見えますが、(3, 2)の次の手の可能性もあり得るのではないかという事です。
(0, 0) -> (1, 0) -> (2, 0) -> (3, 0) -> (4, 0) -> (5, 0) // 0行目 L> (3, 1) -> (4, 1) -> (5, 1) // 1行目 L> (2, 2) -> (3, 2) -> (4, 2) -> (5, 2) // 2行目 L> (4, 3) -> (5, 3) // 3行目 L> (4, 4) -> (5, 4) // 4行目
僕の理解力不足であればいいのですが。もしそうでしたらご指摘ください。
で、代案としてstruct着手に自分の通番(id)と前の着手の通番(id)を持っておくといいんじゃないかという気がしています。
まだ見えてる訳ではないのですが、その路線で進めてみようと思っています。
進展があったらご報告します。
引き続き回答、アドバイス等あればお待ちしています。
追記(2019.11.26 8:41)
MasatoUchidaさんのアドバイスを手がかりにデータの持ち方を変えてみました。
簡単のため石の色や座標は省略し、着手番号(id)と次の着手(idの配列)を持たせることにしました。
structだとエラーが出るのでclassにしてみました。
それが下のコードです。
swift
1var 通番: Int = 0 2class 着手 { 3 var 着手番号: Int 4 var 次の着手: [着手] 5 init() { 6 self.着手番号 = 通番 7 self.次の着手 = [] 8 通番 += 1 9 } 10 func append(次の着手: 着手) { 11 self.次の着手.append(次の着手) 12 } 13 func print() { 14 if 次の着手.count == 0 { 15 Swift.print(self.着手番号) 16 } else { 17 for i in 0 ..< 次の着手.count { 18 if 次の着手[i].着手番号 == 4 { 19 } 20 Swift.print(self.着手番号, " - ", separator: "", terminator: "") 21 次の着手[i].print() 22 } 23 } 24 } 25} 26 27let 棋譜 = 着手() 28let A = 着手() 29let B = 着手() 30let C = 着手() 31let D = 着手() 32let E = 着手() 33let F = 着手() 34let G = 着手() 35let H = 着手() 36let I = 着手() 37let J = 着手() 38let K = 着手() 39棋譜.append(次の着手: A) 40A.append(次の着手: B) 41A.append(次の着手: C) 42B.append(次の着手: D) 43D.append(次の着手: G) 44D.append(次の着手: H) 45D.append(次の着手: I) 46C.append(次の着手: E) 47C.append(次の着手: F) 48F.append(次の着手: J) 49F.append(次の着手: K) 50 51棋譜.print()
これを実行すると、
0 - 1 - 2 - 4 - 7 4 - 8 4 - 9 1 - 3 - 5 3 - 6 - 10 6 - 11
と表示されます。
本当は
0 - 1 - 2 - 4 - 7 | L 8 | L 9 L 3 - 5 L 6 - 10 L 11
のように表示させたいのですが、それは本筋とは関係ないので良しとします。
上のコードにはundo()は書いてありませんが、一つ戻るだけじゃなく任意の着手にジャンプできるので基本この形でいいのかなと思ってます。
アドバイス、回答、ありがとうございました。
ソースが汚い、無駄があるなどありましたら是非お知らせください。
回答1件
あなたの回答
tips
プレビュー