[英]Tic-Tac-Toe game — Regex wants more characters than in the 3x3 board?
[英]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 C和alexsalo的混合體,特別是利用 alexsalo 的 X 和 O 的位板表示,但使用與 Gary C 的方法類似的技術來確定獲勝的 position。這需要更多的位旋轉來確定是否最后一步是贏家,但很容易擴展為 N x N tic-tac-toe ...
使用 Gary C 的方案,N x N 井字游戲將涉及代表行、列和對角線的位,以及間隔位。 X 和 O 都需要這種按位表示。 通過一些數學運算,我們可以確定每個玩家所需的位數。 首先,表示行、列和對角線的位數……
...加上代表間隔符的位數...
下表顯示了 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 板來說,表示 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 的表示將是...
通過簡單地將一行中的位運算在一起,然后檢查結果是否非零,確定玩家是否在同一行中有 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.