簡體   English   中英

查找(nxn)井字棋盤中所有單元格二進制值的算法?

[英]Algorithm to find binary value of all cells in (n x n) Tic-tac-toe board?

我從Gary C 找到了這個很好的答案,它使用按位運算符和最少數量的比較來檢查井字游戲的勝利。 唯一的問題是每個單元格的值需要在開始時進行硬編碼。

例如,他說 cell[0][0] 將被賦予值

100 0 000 0 000 0 100 0 000 0 000 100 0 000 0

其中每組 3 位代表

row1 0 row2 0 row3 0 column1 0 column2 0 column3 0 diag 0 antiDiag 0

, 並且接下來的 0 充當填充(對於他的解決方案的 rest 很重要)。 這是因為cell[0][0]占據了row1、column1以及對角線的前position。

這個任務雖然麻煩,但對於 3x3 板來說肯定是可行的,但是問題是如何對一般的 nxn 板執行此操作。

我假設我們必須從 go 中獲取的輸入是:a) 單元格的行索引和列索引。 b) n 的值,它告訴我們每個組必須有 n 位,以及一個額外的位用於填充。

我知道這是一個非常具體的解決方案中的一個非常具體的場景。 不過他的回答相當精彩,讓人不禁好奇到底是如何一路落實的。 半答案和建議也很受歡迎,因為這將有助於討論。

簡短的回答...

相信最好的方法是Gary Calexsalo的混合體,特別是利用 alexsalo 的 X 和 O 的位板表示,但使用與 Gary C 的方法類似的技術來確定獲勝的 position。這需要更多的位旋轉來確定是否最后一步是贏家,但很容易擴展為 N x N tic-tac-toe ...

N x N tic-tac-toe 所需的位數 Gary C

使用 Gary C 的方案,N x N 井字游戲將涉及代表行、列和對角線的位,以及間隔位。 X 和 O 都需要這種按位表示。 通過一些數學運算,我們可以確定每個玩家所需的位數。 首先,表示行、列和對角線的位數……

  • N 行 * N 列 *(1 組行 + 1 組列)+ 2 診斷 * N

...加上代表間隔符的位數...

  • N 行 + N 列 + 2 個診斷

下表顯示了 N x N 版本的井字游戲每位玩家所需的總位數:

所需位 墊片 全部的
3個 24 8個 32
4個 40 10 50
5個 60 12 72
6個 84 14 98
7 112 16 128
8個 144 18 162
9 180 20 200
10 220 22 242
11 264 24 288
12 312 26 338
13 364 28 392
14 420 30 450
15 480 32 512
16 544 34 578

除了使用 BigInt 之外,Javascript 支持的最大 integer 是 64 位 BigUint64Array 值類型,因此已經從 5 x 5 板開始,即將進行的移位操作將已經需要在各個二進制數之間進行移位。 當然可以使用 BigInt,盡管可能會影響性能。

alexsalo 的 N x N 井字游戲所需的位數

alexsalo 的方案只需要每方塊一位,因此對於 N x N 板來說,表示 X 或 O 的位數是......

所需位
3個 9
4個 16
5個 25
6個 36
7 49
8個 64
9 81
10 100
11 121
12 144
13 169
14 196
15 225
16 256

在這種情況下,單個 BigUint64Array 值最多可以處理 8 x 8 板。 同樣,在那之后,除非使用 BigInt,否則將需要多個二進制值。

測試獲勝者

當然,權衡是使用更簡單的數據結構,對獲勝者的測試成本更高一些,但如前所述,對於可變大小的井字棋盤,這更容易擴展。 例如,一個 4 x 4 的井字棋盤將用於顯示算法,以按行、列和對角線檢查獲勝者。 這些算法本質上是 O(N) 的,因此很容易為更大的板子進行算法擴展,尤其是在使用 BigInt 時。 例如,如果使用 BigUint64Array 值,那么任何位移都將涉及底層 integer 值之間的溢出,從而使按位算法稍微復雜一些,但並不過分。

例如,對 64 位 Uint 進行位移很簡單,但對於 128 位 Uint 則不然,因為沒有 128 位數字的內部表示,除了 BigInt,在這種情況下可能會影響性能。 用兩個 64 位 Uint 表示一個 128 位 Uint 是可能的,但是,如果在第一個 64 位 Uint 中右移 10 位,則必須將最右邊的 10 位移入第二個 64 位 Uint ...

回到通過示例測試獲勝者,假設我們的 4 x 4 棋盤游戲 state 目前......

     a  b  c  d
     -  -  -  -
 1:  O  X  O  O
 2:     X  X  X
 3:  X  X  O  O
 4:     X

那么 X 和 O 的表示將是...

  • X位板:0100 0111 1100 0100
  • O位板:1011 0000 0011 0000

檢查行中的獲勝者...

通過簡單地將一行中的位運算在一起,然后檢查結果是否非零,確定玩家是否在同一行中有 4 個。

( bitboard >>> 3) & (bitboard >>> 2) & (bitboard >>> 1) & bitboard & 0001 0001 0001 0001

例如,連續檢查 X 位板是否有 4 個...

                  abcd abcd abcd abcd 
 -------------------------------------
 bitboard >>> 3:  0000 1000 1111 1000
 bitboard >>> 2:  0001 0001 1111 0001
 bitboard >>> 1:  0010 0011 1110 0010
 bitboard:        0100 0111 1100 0100
 mask:            0001 0001 0001 0001
 -------------------------------------
 ANDed together:  0000 0000 0000 0000  => there are no 4 X's in a single Row. 

向下看每一列“d”將顯示與位板行相同的序列。 即,第一個設置列“d”是 0100,第二個是 0111,等等,這與代表每一行的 4 位的 X 位板組相同......

還要注意,如果沒有 Gary C 間隔位,即使有 5 個連續的 1,我們仍然會得到正確的結果,因為這些位實際上被移位並與“d”列進行了邏輯運算,然后最終掩碼確保我們只考慮“d”列',而不是第三組中的誤報,其中“a”列似乎連續有 4 個。

檢查列中的獲勝者

通過簡單地對同一列中的所有位進行移位和 AND 運算,然后檢查結果是否非零,確定玩家是否在一列中有 4。

bitboard & (bitboard >>> 4) & (bitboard >>> 8) & (bitboard >>> 12)

例如,檢查列中 4 的 X 位板...

                   abcd abcd abcd abcd 
 -------------------------------------
 bitboard:         0100 0111 0100 0100
 bitboard >>> 4:   0000 0100 0111 0100
 bitboard >>> 8:   0000 0000 0100 0111
 bitboard >>> 12:  0000 0000 0000 0100
 -------------------------------------
 ANDed together:   0000 0000 0000 0100  => column 'b' has 4 in a col.                

請注意,確定玩家是否在 Column 中有 4 個的邏輯可以優化為 O(log N)...

檢查對角線的贏家

類似於檢查列中的獲勝者,而是將對角線位合並到列“a”和“d”中......

(bitboard >>> 12) & (bitboard << 1 | bitboard >>> 1) >>> 8 & (bitboard << 2 | bitboard >>> 2) >>> 4 & (bitboard << 3 | bitboard >>> 3)

...和列檢查一樣,結果將是最低 4 位,特別是列“a”和“d”。

底層實現...

建議從位板的 BigInt 表示開始,因為這將簡化基於 N x N 板敲定特定算法的工作,然后在尋求最佳性能時移植到使用 32 或 64 位 Uint。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM