ms_toollib/
utils.rs

1use itertools::Itertools;
2#[cfg(any(feature = "py", feature = "rs"))]
3use rand::seq::SliceRandom;
4#[cfg(any(feature = "py", feature = "rs"))]
5use rand::thread_rng;
6use std::cmp::{max, min};
7use std::vec;
8// use std::convert::TryInto;
9#[cfg(feature = "js")]
10use getrandom::getrandom;
11
12use crate::safe_board;
13use crate::safe_board::BoardSize;
14use crate::ENUM_LIMIT;
15
16// 整个模块是最底层的一些小工具,如埋雷、局面分块、计算3BV等
17
18/// 输入局面,计算空,即0的8连通域数
19pub fn cal_op<T>(board_raw: &T) -> usize
20where
21    T: std::ops::Index<usize> + safe_board::BoardSize,
22    T::Output: std::ops::Index<usize, Output = i32>,
23{
24    let row = board_raw.get_row();
25    let column = board_raw.get_column();
26    let mut board = vec![vec![0; column]; row];
27    for i in 0..row {
28        for j in 0..column {
29            board[i][j] = board_raw[i][j];
30        }
31    }
32    let mut op = 0;
33    for i in 0..row {
34        for j in 0..column {
35            if board[i][j] == 0 {
36                infect_board(&mut board, i, j);
37                op += 1;
38            }
39        }
40    }
41    op
42}
43
44// Board(x, y)位置的整个空都用数字1填满,仅计算Op用
45fn infect_board(board: &mut Vec<Vec<i32>>, x: usize, y: usize) {
46    let row = board.len();
47    let column = board[0].len();
48    board[x][y] = 1;
49    for i in max(1, x) - 1..min(row, x + 2) {
50        for j in max(1, y) - 1..min(column, y + 2) {
51            if board[i][j] == 0 {
52                infect_board(board, i, j);
53            }
54        }
55    }
56}
57
58/// 输入局面,计算岛  
59pub fn cal_isl<T>(raw_board: &T) -> usize
60where
61    T: std::ops::Index<usize> + safe_board::BoardSize,
62    T::Output: std::ops::Index<usize, Output = i32>,
63{
64    let row = raw_board.get_row();
65    let column = raw_board.get_column();
66    let mut board = vec![vec![1; column]; row];
67    for i in 0..row {
68        for j in 0..column {
69            if raw_board[i][j] <= 0 {
70                continue;
71            }
72            let mut flag = true;
73            'outer: for m in max(1, i) - 1..min(row, i + 2) {
74                for n in max(1, j) - 1..min(column, j + 2) {
75                    if raw_board[m][n] == 0 {
76                        flag = false;
77                        break 'outer;
78                    }
79                }
80            }
81            if flag {
82                board[i][j] = 0;
83            }
84        }
85    }
86    cal_op(&board)
87}
88
89/// 计算每个数字出现的次数  
90pub fn cal_cell_nums<T>(raw_board: &T) -> [usize; 9]
91where
92    T: std::ops::Index<usize> + BoardSize,
93    T::Output: std::ops::Index<usize, Output = i32>,
94{
95    let row = raw_board.get_row();
96    let column = raw_board.get_column();
97    let mut ans = [0; 9];
98    for i in 0..row {
99        for j in 0..column {
100            if raw_board[i][j] < 0 {
101                continue;
102            }
103            ans[raw_board[i][j] as usize] += 1;
104        }
105    }
106    ans
107}
108
109/// 根据游戏局面生成矩阵,不分块。输入必须保证是合法的游戏局面。  
110/// - 注意:优点是含义明确,便于理解。但不分块
111pub fn refresh_matrix(
112    game_board: &Vec<Vec<i32>>,
113) -> (Vec<Vec<i32>>, Vec<(usize, usize)>, Vec<i32>) {
114    let row = game_board.len();
115    let column = game_board[0].len();
116    let mut MatrixA: Vec<Vec<i32>> = Vec::new();
117    let mut Matrixx: Vec<(usize, usize)> = Vec::new();
118    let mut Matrixb: Vec<i32> = Vec::new();
119    let mut MatrixARowNum = 0;
120    let mut MatrixAColumnNum = 0;
121
122    for i in 0..row {
123        for j in 0..column {
124            if game_board[i][j] > 0 && game_board[i][j] < 10 {
125                let mut flag: bool = false;
126                for m in max(1, i) - 1..min(row, i + 2) {
127                    for n in max(1, j) - 1..min(column, j + 2) {
128                        if game_board[m][n] == 10 {
129                            flag = true;
130                        }
131                    }
132                }
133                if flag {
134                    MatrixA.push(vec![0; MatrixAColumnNum]);
135                    Matrixb.push(game_board[i][j]);
136                    MatrixARowNum += 1;
137                    for m in max(1, i) - 1..min(row, i + 2) {
138                        for n in max(1, j) - 1..min(column, j + 2) {
139                            if game_board[m][n] == 11 {
140                                Matrixb[MatrixARowNum - 1] -= 1
141                            } else if game_board[m][n] == 10 {
142                                let mut flag_exit: bool = false;
143                                for idMatrixx in 0..MatrixAColumnNum {
144                                    if Matrixx[idMatrixx].0 == m && Matrixx[idMatrixx].1 == n {
145                                        flag_exit = true;
146                                        MatrixA[MatrixARowNum - 1][idMatrixx] = 1;
147                                    }
148                                }
149                                if !flag_exit {
150                                    for ii in 0..MatrixARowNum {
151                                        MatrixA[ii].push(0)
152                                    }
153                                    Matrixx.push((m, n));
154                                    MatrixAColumnNum += 1;
155                                    MatrixA[MatrixARowNum - 1][MatrixAColumnNum - 1] = 1;
156                                }
157                            }
158                        }
159                    }
160                }
161            }
162        }
163    }
164    (MatrixA, Matrixx, Matrixb)
165}
166
167/// 根据游戏局面生成矩阵,分段。输入的必须保证是合法的游戏局面。  
168/// - *基于数字生成,矩阵的行可能有重复。  
169pub fn refresh_matrixs(
170    board_of_game: &Vec<Vec<i32>>,
171) -> (
172    Vec<Vec<Vec<i32>>>,
173    Vec<Vec<(usize, usize)>>,
174    Vec<Vec<i32>>,
175    usize,
176    usize,
177) {
178    // 根据游戏局面分块生成矩阵。分段的数据结构是最外面再套一层Vec
179    // board_of_game必须且肯定是正确标雷的游戏局面,但不需要标全,不能标非雷
180    // 矩阵的行和列都可能有重复
181    // unknow_block是未知格子数量, is_minenum是标出的是雷的数量
182    let row = board_of_game.len();
183    let column = board_of_game[0].len();
184    let mut unknow_block = 0;
185    let mut is_minenum = 0;
186    let mut matrix_as = vec![];
187    let mut matrix_xs = vec![];
188    let mut matrix_bs = vec![];
189    let mut all_cell: Vec<(usize, usize)> = vec![]; // 记录所有周围有未打开格子的数字的位置
190    for i in 0..row {
191        for j in 0..column {
192            if board_of_game[i][j] >= 0 && board_of_game[i][j] < 10 {
193                'outer: for m in max(1, i) - 1..min(row, i + 2) {
194                    for n in max(1, j) - 1..min(column, j + 2) {
195                        if board_of_game[m][n] == 10 {
196                            all_cell.push((i, j));
197                            break 'outer;
198                        }
199                    }
200                }
201            } else if board_of_game[i][j] == 10 {
202                // 数内部有几个格子
203                let mut flag = true;
204                for m in max(1, i) - 1..min(row, i + 2) {
205                    for n in max(1, j) - 1..min(column, j + 2) {
206                        if board_of_game[m][n] < 10 {
207                            flag = false;
208                        }
209                    }
210                }
211                if flag {
212                    unknow_block += 1;
213                }
214            } else if board_of_game[i][j] == 11 {
215                is_minenum += 1;
216            }
217        }
218    }
219    let mut p = 0; //指针,代表第几块
220                   // println!("{:?}", all_cell);
221    while !all_cell.is_empty() {
222        matrix_xs.push(vec![]);
223        matrix_bs.push(vec![]);
224        let x_0 = all_cell[0].0;
225        let y_0 = all_cell[0].1;
226        let mut num_cells = vec![]; // 记录了当前段的数字格的坐标
227        let mut temp_cells = vec![]; // 记录了待查找的数字格的坐标
228        let mut flag_num = 0;
229        for m in max(1, x_0) - 1..min(row, x_0 + 2) {
230            for n in max(1, y_0) - 1..min(column, y_0 + 2) {
231                if board_of_game[m][n] == 10 {
232                    matrix_xs[p].push((m, n));
233                }
234                if board_of_game[m][n] == 11 {
235                    flag_num += 1;
236                }
237            }
238        }
239        matrix_bs[p].push(board_of_game[x_0][y_0] - flag_num);
240        num_cells.push((x_0, y_0));
241        temp_cells.push((x_0, y_0));
242        while !temp_cells.is_empty() {
243            let x_e = temp_cells[0].0;
244            let y_e = temp_cells[0].1;
245            temp_cells.remove(0);
246            for t in (1..all_cell.len()).rev() {
247                // 从temp_cells中拿出一个格子,找出与其相邻的所有格子,加入temp_cells和matrix_bs、matrix_xs
248                let x_t = all_cell[t].0;
249                let y_t = all_cell[t].1;
250                if x_t >= x_e + 3 || x_e >= x_t + 3 || y_t >= y_e + 3 || y_e >= y_t + 3 {
251                    continue;
252                }
253                let mut flag_be_neighbor = false;
254                for m in max(1, max(x_t, x_e)) - 1..min(row, min(x_t + 2, x_e + 2)) {
255                    for n in max(1, max(y_t, y_e)) - 1..min(column, min(y_t + 2, y_e + 2)) {
256                        if board_of_game[m][n] == 10 {
257                            flag_be_neighbor = true;
258                            break;
259                        }
260                    }
261                } // 从局面上看,如果两个数字有相同的未知格子,那么就会分到同一块
262                if flag_be_neighbor {
263                    let mut flag_num = 0;
264                    for m in max(1, x_t) - 1..min(row, x_t + 2) {
265                        for n in max(1, y_t) - 1..min(column, y_t + 2) {
266                            if board_of_game[m][n] == 10 {
267                                if !matrix_xs[p].contains(&(m, n)) {
268                                    matrix_xs[p].push((m, n));
269                                }
270                            }
271                            if board_of_game[m][n] == 11 {
272                                flag_num += 1;
273                            }
274                        }
275                    }
276                    matrix_bs[p].push(board_of_game[x_t][y_t] - flag_num);
277                    num_cells.push((x_t, y_t));
278                    temp_cells.push(all_cell[t]);
279                    all_cell.remove(t);
280                }
281            }
282        }
283        matrix_as.push(vec![vec![0; matrix_xs[p].len()]; matrix_bs[p].len()]);
284        for i in 0..num_cells.len() {
285            for j in 0..matrix_xs[p].len() {
286                if num_cells[i].0 <= matrix_xs[p][j].0 + 1
287                    && matrix_xs[p][j].0 <= num_cells[i].0 + 1
288                    && num_cells[i].1 <= matrix_xs[p][j].1 + 1
289                    && matrix_xs[p][j].1 <= num_cells[i].1 + 1
290                {
291                    matrix_as[p][i][j] = 1;
292                }
293            }
294        }
295        all_cell.remove(0);
296        p += 1;
297    }
298    (matrix_as, matrix_xs, matrix_bs, unknow_block, is_minenum)
299}
300
301/// 根据游戏局面生成矩阵,分段、且分块。输入的必须保证是合法的游戏局面。  
302pub fn refresh_matrixses(
303    board_of_game: &Vec<Vec<i32>>,
304) -> (
305    Vec<Vec<Vec<Vec<i32>>>>,
306    Vec<Vec<Vec<(usize, usize)>>>,
307    Vec<Vec<Vec<i32>>>,
308) {
309    let row = board_of_game.len();
310    let column = board_of_game[0].len();
311    let mut Ases = vec![];
312    let mut xses = vec![];
313    let mut bses = vec![];
314    let (mut As, mut xs, mut bs, _, _) = refresh_matrixs(board_of_game);
315    if As.len() == 1 {
316        // 不可能为0,至少为1
317        return (vec![As], vec![xs], vec![bs]);
318    }
319    // 邻接矩阵
320    let mut adjacency_matrix = vec![vec![false; As.len()]; As.len()];
321    // 局面的复刻,用于标记遍历过的格子
322    let mut board_mark = board_of_game.clone();
323    let mut cell_10 = vec![];
324    for i in 0..row {
325        for j in 0..column {
326            if board_mark[i][j] == 10 {
327                board_mark[i][j] = 21;
328                let mut flag_c = false;
329                let mut buffer = vec![];
330                buffer.push((i, j));
331                // 标志是否搜索完的缓冲区
332                while !buffer.is_empty() {
333                    let (t_i, t_j) = buffer.pop().unwrap();
334                    let mut flag_is_side = false;
335                    for m in max(1, t_i) - 1..min(row, t_i + 2) {
336                        for n in max(1, t_j) - 1..min(column, t_j + 2) {
337                            if board_mark[m][n] == 10 {
338                                board_mark[m][n] = 21;
339                                buffer.push((m, n));
340                            } else if board_mark[m][n] < 10 {
341                                flag_is_side = true;
342                            }
343                        }
344                    }
345                    if flag_is_side {
346                        if !flag_c {
347                            cell_10.push(vec![]);
348                            flag_c = true;
349                        }
350                        cell_10.last_mut().unwrap().push((t_i, t_j));
351                    }
352                }
353            } else {
354                continue;
355            }
356        }
357    }
358    // println!("{:?}", cell_10);
359    if cell_10.len() == 1 {
360        // 不可能为0,至少为1
361        return (vec![As], vec![xs], vec![bs]);
362    }
363    for mut block in cell_10 {
364        let mut seed_id = -1;
365        while !block.is_empty() {
366            let seed = block.pop().unwrap();
367            let t = xs.iter().position(|r| r.contains(&seed)).unwrap();
368            if seed_id >= 0 {
369                adjacency_matrix[seed_id as usize][t] = true;
370                adjacency_matrix[t][seed_id as usize] = true;
371            }
372            seed_id = t as i32;
373            block.retain(|x| !xs[seed_id as usize].contains(x))
374        }
375    } // 整理完邻接矩阵。无向图。
376    for i in 0..As.len() {
377        if As[i].is_empty() {
378            continue;
379        }
380        Ases.push(vec![]);
381        xses.push(vec![]);
382        bses.push(vec![]);
383        let mut buffer = vec![i];
384
385        while !buffer.is_empty() {
386            let t = buffer.pop().unwrap();
387            Ases.last_mut().unwrap().push(vec![]);
388            Ases.last_mut()
389                .unwrap()
390                .last_mut()
391                .unwrap()
392                .append(&mut As[t]);
393            xses.last_mut().unwrap().push(vec![]);
394            xses.last_mut()
395                .unwrap()
396                .last_mut()
397                .unwrap()
398                .append(&mut xs[t]);
399            bses.last_mut().unwrap().push(vec![]);
400            bses.last_mut()
401                .unwrap()
402                .last_mut()
403                .unwrap()
404                .append(&mut bs[t]);
405            for idj in t..As.len() {
406                if adjacency_matrix[t][idj] {
407                    buffer.push(idj);
408                }
409            }
410        }
411    }
412    (Ases, xses, bses)
413}
414
415// 获取0~limit-1范围内的随机整数
416// 用于js平台
417#[cfg(feature = "js")]
418pub fn get_random_int(limit: usize) -> usize {
419    if limit == 0 {
420        return 0;
421    }
422    let mut a = [0, 0, 0, 0];
423    let mut t;
424    loop {
425        getrandom(&mut a).unwrap();
426        // println!("{:?}", a);
427        t = (a[0] as usize) << 24 ^ (a[1] as usize) << 16 ^ (a[2] as usize) << 8 ^ (a[3] as usize);
428        if t < (0b11111111_11111111_11111111_11111111 / limit * limit) {
429            break;
430        }
431    }
432    t % limit
433}
434
435#[cfg(feature = "js")]
436pub trait js_shuffle {
437    fn shuffle_(&mut self);
438}
439
440#[cfg(feature = "js")]
441impl js_shuffle for Vec<i32> {
442    fn shuffle_(&mut self) {
443        // 存疑!!!!!
444        let l = self.len();
445        for i in 1..l {
446            let id = get_random_int(i + 1);
447            let t = self[i];
448            self[i] = self[id];
449            self[id] = t;
450        }
451    }
452}
453
454/// 一维埋雷,给局部埋雷,完全随机。
455/// - 需要埋雷的区域的面积,雷数。
456/// - 例如,高级标准埋雷时,area = 16*30-1
457pub fn get_board_1d(area: usize, minenum: usize) -> Vec<i32> {
458    let mut board_1d: Vec<i32> = vec![];
459    board_1d.reserve(area);
460    board_1d = vec![0; area - minenum];
461    board_1d.append(&mut vec![-1; minenum]);
462    #[cfg(any(feature = "py", feature = "rs"))]
463    let mut rng = thread_rng();
464
465    #[cfg(any(feature = "py", feature = "rs"))]
466    board_1d.shuffle(&mut rng);
467
468    #[cfg(feature = "js")]
469    board_1d.shuffle_();
470    board_1d
471}
472
473/// 根据起手不开空的规则,把一维的局面转换成二维的。
474pub fn trans_board_1d_2d_op(
475    board_1d: &Vec<i32>,
476    row: usize,
477    column: usize,
478    x0: usize,
479    y0: usize,
480) -> Vec<Vec<i32>> {
481    let mut board = vec![vec![0; column]; row];
482    let mut i = 0;
483    for x in 0..row {
484        for y in 0..column {
485            if x <= x0 + 1 && x0 <= x + 1 && y <= y0 + 1 && y0 <= y + 1 {
486                continue;
487            }
488            if board_1d[i] < 0 {
489                board[x][y] = -1;
490                for j in max(1, x) - 1..min(row, x + 2) {
491                    for k in max(1, y) - 1..min(column, y + 2) {
492                        if board[j][k] >= 0 {
493                            board[j][k] += 1
494                        }
495                    }
496                }
497            }
498            i += 1;
499        }
500    }
501    board
502}
503
504/// 通用标准埋雷引擎。
505/// - 标准埋雷规则:起手位置非雷,其余位置的雷服从均匀分布。
506/// - 输出:二维的局面,其中0代表空,1~8代表1~8,-1代表雷。
507pub fn laymine(row: usize, column: usize, minenum: usize, x0: usize, y0: usize) -> Vec<Vec<i32>> {
508    let board1_dim = get_board_1d(row * column - 1, minenum);
509
510    let mut board1_dim_2: Vec<i32> = vec![];
511    board1_dim_2.reserve(row * column);
512    let pointer = x0 + y0 * row;
513    for i in 0..pointer {
514        board1_dim_2.push(board1_dim[i]);
515    }
516    board1_dim_2.push(0);
517    for i in pointer..(row * column - 1) {
518        board1_dim_2.push(board1_dim[i]);
519    }
520    let mut board: Vec<Vec<i32>> = vec![vec![0; column]; row];
521    for i in 0..(row * column) {
522        if board1_dim_2[i] < 0 {
523            let x = i % row;
524            let y = i / row;
525            board[x][y] = -1;
526            for j in max(1, x) - 1..min(row, x + 2) {
527                for k in max(1, y) - 1..min(column, y + 2) {
528                    if board[j][k] >= 0 {
529                        board[j][k] += 1;
530                    }
531                }
532            }
533        }
534    }
535    board
536}
537
538/// 通用win7规则埋雷引擎。
539/// - win7规则:起手位置开空,其余位置的雷服从均匀分布。
540/// - 输出:二维的局面,其中0代表空,1~8代表1~8,-1代表雷。
541pub fn laymine_op(
542    row: usize,
543    column: usize,
544    minenum: usize,
545    x0: usize,
546    y0: usize,
547) -> Vec<Vec<i32>> {
548    let mut area_op = 9;
549    if x0 == 0 || y0 == 0 || x0 == row - 1 || y0 == column - 1 {
550        if x0 == 0 && y0 == 0
551            || x0 == 0 && y0 == column - 1
552            || x0 == row - 1 && y0 == 0
553            || x0 == row - 1 && y0 == column - 1
554        {
555            area_op = 4;
556        } else {
557            area_op = 6;
558        }
559    }
560    let area = row * column - area_op;
561    let board_1d = get_board_1d(area, minenum);
562    trans_board_1d_2d_op(&board_1d, row, column, x0, y0)
563}
564
565pub fn cal_bbbv_on_island<T>(board: &T) -> usize
566where
567    T: std::ops::Index<usize> + safe_board::BoardSize,
568    T::Output: std::ops::Index<usize, Output = i32>,
569{
570    // 计算除空以外的3BV
571    let row = board.get_row();
572    let column = board.get_column();
573    let mut Num3BVonIsland = 0;
574    for i in 0..row {
575        for j in 0..column {
576            if board[i][j] > 0 {
577                let mut flag: bool = true;
578                for x in max(1, i) - 1..min(row, i + 2) {
579                    for y in max(1, j) - 1..min(column, j + 2) {
580                        if board[x][y] == 0 {
581                            flag = false;
582                        }
583                    }
584                }
585                if flag {
586                    Num3BVonIsland += 1;
587                }
588            }
589        }
590    }
591    Num3BVonIsland
592}
593
594/// 计算局面的3BV
595pub fn cal_bbbv<T>(board: &T) -> usize
596where
597    T: std::ops::Index<usize> + safe_board::BoardSize,
598    T::Output: std::ops::Index<usize, Output = i32>,
599{
600    cal_bbbv_on_island(board) + cal_op(board)
601}
602
603/// 依据左击位置刷新局面。如踩雷,标上或14、15标记
604/// - 注意:兼容12标记符
605pub fn refresh_board<T>(
606    board: &T,
607    boardofGame: &mut Vec<Vec<i32>>,
608    mut clicked_poses: Vec<(usize, usize)>,
609) where
610    T: std::ops::Index<usize> + safe_board::BoardSize,
611    T::Output: std::ops::Index<usize, Output = i32>,
612{
613    let row = board.get_row();
614    let column = board.get_column();
615    // 是否踩雷
616    let mut loss_flag = false;
617    while let Some(top) = clicked_poses.pop() {
618        let (i, j) = top;
619        if board[i][j] > 0 {
620            boardofGame[i][j] = board[i][j];
621        } else if board[i][j] == 0 {
622            boardofGame[i][j] = 0;
623            for m in max(1, i) - 1..min(row, i + 2) {
624                for n in max(1, j) - 1..min(column, j + 2) {
625                    if (i != m || j != n) && (boardofGame[m][n] == 10 || boardofGame[m][n] == 12) {
626                        clicked_poses.push((m, n));
627                    }
628                }
629            }
630        } else {
631            boardofGame[i][j] = 15; // 标红雷,此处是雷,且踩到了
632            loss_flag = true;
633        }
634    }
635    // 标叉雷
636    if loss_flag {
637        for i in 0..row {
638            for j in 0..column {
639                if boardofGame[i][j] == 11 && board[i][j] != -1 {
640                    boardofGame[i][j] = 14; // 叉雷,即标错的雷
641                }
642            }
643        }
644    }
645}
646
647#[derive(Clone, Debug)]
648pub struct BigNumber {
649    // 科学计数法表示的大数字
650    // 必定大于等于1,a必定满足小于10大于等于1
651    pub a: f64,
652    pub b: i32,
653}
654
655impl BigNumber {
656    fn a_become_smaller_than(&mut self, thrd: f64) {
657        // 如果big_number大于thrd
658        // 把位数都放到指数上,使其满足a小于10大于等于1
659        if self.a < thrd {
660            return;
661        }
662        while self.a >= 10.0 {
663            self.a /= 10.0;
664            self.b += 1;
665        }
666    }
667    pub fn mul_usize(&mut self, k: usize) {
668        // 与usize相乘
669        if k == 0 {
670            self.a = 0.0;
671            self.b = 1;
672        } else {
673            self.a *= k as f64;
674            self.a_become_smaller_than(10.0);
675        }
676    }
677    pub fn mul_big_number(&mut self, k: &BigNumber) {
678        // 与big_number相乘, big_number必须至少为1
679        self.a *= k.a;
680        self.b += k.b;
681        self.a_become_smaller_than(10.0);
682    }
683    pub fn add_big_number(&mut self, k: &BigNumber) {
684        let mut ka = k.a;
685        let mut kb = k.b;
686        while self.b > kb {
687            ka /= 10.0;
688            kb += 1;
689        }
690        while self.b < kb {
691            self.a /= 10.0;
692            self.b += 1;
693        }
694        self.a += ka;
695        self.a_become_smaller_than(10.0);
696    }
697    pub fn div_big_num(&mut self, k: &BigNumber) -> f64 {
698        // 计算大数相除
699        let mut ans = self.a / k.a;
700        while self.b < k.b {
701            ans /= 10.0;
702            self.b += 1;
703        }
704        while self.b > k.b {
705            ans *= 10.0;
706            self.b -= 1;
707        }
708        ans
709    }
710    pub fn div_usize(&mut self, k: usize) {
711        // 计算大数除以正整数。这里被除数大于等于0;除数大于等于1
712        if self.a < 1e-8 && self.b == 1 {
713            return;
714        } else {
715            self.a /= k as f64;
716            while self.a < 1.0 {
717                self.a *= 10.0;
718                self.b -= 1;
719            }
720        }
721    }
722}
723
724pub fn C(n: usize, k: usize) -> BigNumber {
725    // n不超过1e10
726    if n < k + k {
727        return C(n, n - k);
728    };
729    let maximum_limit: f64 = 1e208;
730    let mut c = BigNumber { a: 1.0, b: 0 };
731    for i in 0..k {
732        c.a *= (n - i) as f64;
733        c.a /= (i + 1) as f64;
734        c.a_become_smaller_than(maximum_limit);
735    }
736    c.a_become_smaller_than(10.0);
737    c
738}
739
740pub fn C_query<T, U>(n: T, k: U) -> usize
741where
742    T: Into<usize>,
743    U: Into<usize>,
744{
745    // 查表计算8以内小数字的组合数
746    let a = [
747        [1, 0, 0, 0, 0, 0, 0, 0, 0],
748        [1, 1, 0, 0, 0, 0, 0, 0, 0],
749        [1, 2, 1, 0, 0, 0, 0, 0, 0],
750        [1, 3, 3, 1, 0, 0, 0, 0, 0],
751        [1, 4, 6, 4, 1, 0, 0, 0, 0],
752        [1, 5, 10, 10, 5, 1, 0, 0, 0],
753        [1, 6, 15, 20, 15, 6, 1, 0, 0],
754        [1, 7, 21, 35, 35, 21, 7, 1, 0],
755        [1, 8, 28, 56, 70, 56, 28, 8, 1],
756    ];
757    a[n.into()][k.into()]
758}
759
760pub fn combine(
761    MatrixA: &Vec<Vec<i32>>,
762    Matrixx: &Vec<(usize, usize)>,
763) -> (Vec<Vec<i32>>, Vec<(usize, usize)>, Vec<Vec<usize>>) {
764    // 检查地位完全相同的格子,全部返回。例如[[3,1,2],[0,5],[4],[6]]
765    // MatrixA不能为空
766    // 并在内部更改矩阵,合并重复的列
767    let mut matrixA_squeeze = MatrixA.clone();
768    let mut matrixx_squeeze = Matrixx.clone();
769    let cells_num = matrixx_squeeze.len();
770    let mut pair_cells = vec![];
771    let mut del_cells = vec![]; // 由于重复需要最后被删除的列
772    for i in 0..cells_num {
773        pair_cells.push(vec![i]);
774        for j in i + 1..cells_num {
775            if !matrixA_squeeze.iter().any(|x| x[i] != x[j]) {
776                pair_cells[i].push(j);
777                del_cells.push(j);
778            }
779        }
780    }
781    del_cells.sort_by(|a, b| b.cmp(&a));
782    del_cells.dedup();
783    for i in del_cells {
784        for r in 0..matrixA_squeeze.len() {
785            matrixA_squeeze[r].remove(i);
786        }
787        matrixx_squeeze.remove(i);
788        pair_cells.remove(i);
789    }
790    let cell_squeeze_num = pair_cells.len();
791    for i in 0..cell_squeeze_num {
792        let k = pair_cells[i].len() as i32;
793        for r in 0..matrixA_squeeze.len() {
794            matrixA_squeeze[r][i] *= k;
795        }
796    }
797    (matrixA_squeeze, matrixx_squeeze, pair_cells)
798}
799
800
801/// 枚举法求解矩阵,返回所有的解
802pub fn cal_all_solution(
803    matrixA: &Vec<Vec<i32>>,
804    Matrixb: &Vec<i32>,
805) -> Vec<Vec<u8>> {
806    let column = matrixA[0].len();
807    let row = matrixA.len();
808    let mut enum_comb_table: Vec<Vec<u8>> = vec![vec![0; column]];
809    let mut not_enum_cell: Vec<bool> = vec![true; column]; // 记录每个位置是否被枚举过,true是没有被枚举过
810    let mut enum_cell_table: Vec<Vec<usize>> = vec![];
811    for row in 0..row {
812        let mut new_enum_cell = vec![]; // 当前条件涉及的新格子
813        let mut enum_cell = vec![]; // 当前条件涉及的所有格子
814        let mut new_enum_max = vec![];
815        for j in 0..column {
816            if matrixA[row][j] > 0 {
817                enum_cell.push(j);
818                if not_enum_cell[j] {
819                    not_enum_cell[j] = false;
820                    new_enum_cell.push(j);
821                    new_enum_max.push(matrixA[row][j]);
822                }
823            }
824        }
825        // 第一步,整理出当前条件涉及的所有格子,以及其中哪些是新格子
826        let mut new_enum_table = (0..new_enum_cell.len())
827            .map(|i| 0..new_enum_max[i] + 1)
828            .multi_cartesian_product()
829            .collect::<Vec<_>>();
830        new_enum_table.retain(|x| x.iter().sum::<i32>() <= Matrixb[row]);
831        // 第二步,获取这些新枚举到的格子的所有满足周围雷数约束的情况,即子枚举表
832        if new_enum_table.is_empty() {
833            enum_comb_table.retain(|item| {
834                enum_cell
835                    .iter()
836                    .fold(0, |sum: u8, i: &usize| sum + item[*i])
837                    == Matrixb[row] as u8
838            });
839        // 第三步,若子枚举表为空,不用将子枚举表与主枚举表合并;且只检查主枚举表是否满足当前这条规则,删除一些不满足的
840        } else {
841            let mut flag_1 = true; // 代表新枚举的格子是否需要新增情况
842            let enum_comb_table_len = enum_comb_table.len();
843            for item in new_enum_table {
844                if flag_1 {
845                    for m in 0..new_enum_cell.len() {
846                        for n in 0..enum_comb_table_len {
847                            enum_comb_table[n][new_enum_cell[m]] = item[m] as u8;
848                        }
849                    }
850                    flag_1 = false;
851                } else {
852                    for n in 0..enum_comb_table_len {
853                        let mut one_row_in_new_table = enum_comb_table[n].clone();
854                        for m in 0..new_enum_cell.len() {
855                            one_row_in_new_table[new_enum_cell[m]] = item[m] as u8;
856                        }
857                        enum_comb_table.push(one_row_in_new_table);
858                    }
859                }
860            } // 第四步,若子枚举表非空,先将子枚举表与主枚举表合并
861            let mut equations = vec![];
862            for kk in &enum_cell {
863                for rr in 0..row {
864                    if matrixA[rr][*kk] > 0 {
865                        equations.push(rr);
866                    }
867                }
868            }
869            equations.dedup();
870            // 第五步,再找出本条规则涉及的之前所有的规则的id
871            for equ in equations {
872                enum_comb_table.retain(|item| {
873                    enum_cell_table[equ]
874                        .iter()
875                        .fold(0, |sum: u8, i: &usize| sum + item[*i])
876                        == Matrixb[equ] as u8
877                });
878            }
879            enum_comb_table.retain(|item| {
880                enum_cell
881                    .iter()
882                    .fold(0, |sum: u8, i: &usize| sum + item[*i])
883                    == Matrixb[row] as u8
884            }); // 这段重复了,不过不影响性能,之后优化
885                // 第六步,用本条规则、以及涉及的之前所有规则过滤所有情况
886        }
887        enum_cell_table.push(enum_cell);
888    }
889    enum_comb_table
890}
891
892// pub fn enum_comb(
893//     matrixA_squeeze: &Vec<Vec<i32>>,
894//     matrixx_squeeze: &Vec<(usize, usize)>,
895//     Matrixb: &Vec<i32>,
896// ) -> Vec<Vec<u8>> {
897//     // 拟弃用
898//     // 枚举法求解矩阵,返回所有的解
899//     let column = matrixx_squeeze.len();
900//     let row = matrixA_squeeze.len();
901//     let mut enum_comb_table: Vec<Vec<u8>> = vec![vec![0; column]];
902//     let mut not_enum_cell: Vec<bool> = vec![true; column]; // 记录每个位置是否被枚举过,true是没有被枚举过
903//     let mut enum_cell_table: Vec<Vec<usize>> = vec![];
904//     for row in 0..row {
905//         let mut new_enum_cell = vec![]; // 当前条件涉及的新格子
906//         let mut enum_cell = vec![]; // 当前条件涉及的所有格子
907//         let mut new_enum_max = vec![];
908//         for j in 0..column {
909//             if matrixA_squeeze[row][j] > 0 {
910//                 enum_cell.push(j);
911//                 if not_enum_cell[j] {
912//                     not_enum_cell[j] = false;
913//                     new_enum_cell.push(j);
914//                     new_enum_max.push(matrixA_squeeze[row][j]);
915//                 }
916//             }
917//         }
918//         // 第一步,整理出当前条件涉及的所有格子,以及其中哪些是新格子
919//         let mut new_enum_table = (0..new_enum_cell.len())
920//             .map(|i| 0..new_enum_max[i] + 1)
921//             .multi_cartesian_product()
922//             .collect::<Vec<_>>();
923//         new_enum_table.retain(|x| x.iter().sum::<i32>() <= Matrixb[row]);
924//         // 第二步,获取这些新枚举到的格子的所有满足周围雷数约束的情况,即子枚举表
925//         if new_enum_table.is_empty() {
926//             enum_comb_table.retain(|item| {
927//                 enum_cell
928//                     .iter()
929//                     .fold(0, |sum: u8, i: &usize| sum + item[*i])
930//                     == Matrixb[row] as u8
931//             });
932//         // 第三步,若子枚举表为空,不用将子枚举表与主枚举表合并;且只检查主枚举表是否满足当前这条规则,删除一些不满足的
933//         } else {
934//             let mut flag_1 = true; // 代表新枚举的格子是否需要新增情况
935//             let enum_comb_table_len = enum_comb_table.len();
936//             for item in new_enum_table {
937//                 if flag_1 {
938//                     for m in 0..new_enum_cell.len() {
939//                         for n in 0..enum_comb_table_len {
940//                             enum_comb_table[n][new_enum_cell[m]] = item[m] as u8;
941//                         }
942//                     }
943//                     flag_1 = false;
944//                 } else {
945//                     for n in 0..enum_comb_table_len {
946//                         let mut one_row_in_new_table = enum_comb_table[n].clone();
947//                         for m in 0..new_enum_cell.len() {
948//                             one_row_in_new_table[new_enum_cell[m]] = item[m] as u8;
949//                         }
950//                         enum_comb_table.push(one_row_in_new_table);
951//                     }
952//                 }
953//             } // 第四步,若子枚举表非空,先将子枚举表与主枚举表合并
954//             let mut equations = vec![];
955//             for kk in &enum_cell {
956//                 for rr in 0..row {
957//                     if matrixA_squeeze[rr][*kk] > 0 {
958//                         equations.push(rr);
959//                     }
960//                 }
961//             }
962//             equations.dedup();
963//             // 第五步,再找出本条规则涉及的之前所有的规则的id
964//             for equ in equations {
965//                 enum_comb_table.retain(|item| {
966//                     enum_cell_table[equ]
967//                         .iter()
968//                         .fold(0, |sum: u8, i: &usize| sum + item[*i])
969//                         == Matrixb[equ] as u8
970//                 });
971//             }
972//             enum_comb_table.retain(|item| {
973//                 enum_cell
974//                     .iter()
975//                     .fold(0, |sum: u8, i: &usize| sum + item[*i])
976//                     == Matrixb[row] as u8
977//             }); // 这段重复了,不过不影响性能,之后优化
978//                 // 第六步,用本条规则、以及涉及的之前所有规则过滤所有情况
979//         }
980//         enum_cell_table.push(enum_cell);
981//     }
982//     enum_comb_table
983// }
984
985// fn enumerateSub(Col: usize, minenum: usize) -> Vec<Vec<usize>> {
986//     let mut Out: Vec<Vec<usize>> = vec![];
987//     for i in (0..Col).combinations(minenum) {
988//         Out.push(vec![0; Col]);
989//         let len = Out.len() - 1;
990//         for j in 0..minenum {
991//             Out[len][i[j]] = 1;
992//         }
993//     }
994//     Out
995// }
996
997/// 忘了干嘛用的,有待重构。和弱可猜有关。
998// pub fn enuOneStep(mut AllTable: Vec<Vec<usize>>, TableId: Vec<usize>, b: i32) -> Vec<Vec<usize>> {
999//     // AllTable不能为空
1000//     let mut NewId: Vec<usize> = vec![];
1001//     for i in &TableId {
1002//         if AllTable[0][*i] == 2 {
1003//             NewId.push(*i);
1004//         }
1005//     }
1006//     let mut DelId = vec![];
1007//     for i in 0..AllTable.len() {
1008//         let mut ExtraMine = b;
1009//         for j in &TableId {
1010//             if AllTable[i][*j] == 1 {
1011//                 ExtraMine -= 1;
1012//             }
1013//         }
1014//         if ExtraMine < 0 || ExtraMine as usize > NewId.len() {
1015//             DelId.push(i);
1016//             continue;
1017//         }
1018//         let AddedTable = enumerateSub(NewId.len(), ExtraMine as usize);
1019//         for t in 0..NewId.len() {
1020//             AllTable[i][NewId[t]] = AddedTable[0][t];
1021//         }
1022//         for m in 1..AddedTable.len() {
1023//             AllTable.push(AllTable[i].clone());
1024//             for t in 0..NewId.len() {
1025//                 let len = AllTable.len() - 1;
1026//                 AllTable[len][NewId[t]] = AddedTable[m][t];
1027//             }
1028//         }
1029//     }
1030//     DelId.sort_by(|a, b| b.cmp(&a));
1031//     for i in DelId {
1032//         AllTable.remove(i);
1033//     }
1034//     AllTable
1035// }
1036
1037fn cal_cell_and_equation_map(matrix_a: &Vec<Vec<i32>>) -> (Vec<Vec<usize>>, Vec<Vec<usize>>) {
1038    // cell_to_equation_map是方程中未知数的索引到方程的索引的映射
1039    // 方程中的未知数可以理解成未知的格子,每个方程可以理解成局面中的一个数字
1040    // 也可以理解成矩阵A的稀疏表示
1041    let cells_num = matrix_a[0].len();
1042    let equations_num = matrix_a.len();
1043    let mut cell_to_equation_map = vec![vec![]; cells_num];
1044    let mut equation_to_cell_map = vec![vec![]; equations_num];
1045    for i in 0..equations_num {
1046        for j in 0..cells_num {
1047            if matrix_a[i][j] >= 1 {
1048                equation_to_cell_map[i].push(j);
1049                cell_to_equation_map[j].push(i);
1050            }
1051        }
1052    }
1053    (cell_to_equation_map, equation_to_cell_map)
1054}
1055
1056fn cal_table_minenum_recursion_step(
1057    idx: usize,
1058    current_amount: usize,
1059    table_minenum: &mut [Vec<usize>; 2],
1060    table_cell_minenum: &mut Vec<Vec<usize>>,
1061    // mut upper_limit: usize,
1062    // lower_limit: usize,
1063    matrixA_squeeze: &Vec<Vec<i32>>,
1064    matrix_b: &Vec<i32>,
1065    matrix_b_remain: &mut Vec<i32>,
1066    combination_relationship: &Vec<Vec<usize>>,
1067    cell_to_equation_map: &Vec<Vec<usize>>,
1068    equation_to_cell_map: &Vec<Vec<usize>>,
1069    mine_vec: &mut Vec<usize>,
1070) -> Result<bool, usize> {
1071    // mine_vec: 是雷位置都记录下来,只记录一个索引,可能有重复
1072    let cells_num = matrixA_squeeze[0].len();
1073    if idx >= cells_num {
1074        //终止条件
1075        let total_mines_num: usize = mine_vec.iter().sum();
1076        if total_mines_num >= table_minenum[1].len() {
1077            return Err(5);
1078        }
1079        table_minenum[1][total_mines_num] += current_amount;
1080        for (idn, n) in mine_vec.iter().enumerate() {
1081            table_cell_minenum[total_mines_num][idn] +=
1082                current_amount * n / combination_relationship[idn].len();
1083        }
1084        return Ok(true);
1085    }
1086
1087    let mut upper_limit = combination_relationship[idx].len();
1088    let mut lower_limit = 0usize;
1089    for cell_i in &cell_to_equation_map[idx] {
1090        if matrixA_squeeze[*cell_i][idx] == 0 {
1091            continue;
1092        }
1093        let upper_limit_i = min(
1094            matrix_b_remain[*cell_i],
1095            combination_relationship[idx].len() as i32,
1096        );
1097        let mut lower_limit_i = matrix_b_remain[*cell_i];
1098        for j in &equation_to_cell_map[*cell_i] {
1099            if j > &idx {
1100                lower_limit_i -= combination_relationship[*j].len() as i32;
1101            }
1102        }
1103        if upper_limit_i < upper_limit as i32 {
1104            upper_limit = upper_limit_i as usize;
1105        }
1106        if lower_limit_i > lower_limit as i32 {
1107            lower_limit = lower_limit_i as usize;
1108        }
1109    }
1110
1111    // println!("{:?}, {:?}", lower_limit, upper_limit + 1);
1112    // if lower_limit < upper_limit + 1 {
1113    for u in lower_limit..upper_limit + 1 {
1114        // let b = mine_vec[idx];
1115        mine_vec[idx] = u;
1116        if u > 0 {
1117            for tt in &cell_to_equation_map[idx] {
1118                matrix_b_remain[*tt] -= u as i32;
1119            }
1120        }
1121        let _ = cal_table_minenum_recursion_step(
1122            idx + 1,
1123            current_amount * C_query(combination_relationship[idx].len(), u),
1124            table_minenum,
1125            table_cell_minenum,
1126            &matrixA_squeeze,
1127            &matrix_b,
1128            matrix_b_remain,
1129            &combination_relationship,
1130            &cell_to_equation_map,
1131            &equation_to_cell_map,
1132            mine_vec,
1133        )?;
1134        for tt in &cell_to_equation_map[idx] {
1135            matrix_b_remain[*tt] += u as i32;
1136        }
1137        mine_vec[idx] = 0;
1138    }
1139    // }
1140    Ok(false)
1141}
1142
1143pub fn cal_table_minenum_recursion(
1144    matrixA_squeeze: &Vec<Vec<i32>>,
1145    matrixx_squeeze: &Vec<(usize, usize)>,
1146    matrix_b: &Vec<i32>,
1147    combination_relationship: &Vec<Vec<usize>>,
1148) -> Result<([Vec<usize>; 2], Vec<Vec<usize>>), usize> {
1149    // 递归算法,得到雷数分布表和每格是雷情况数表,顺便计算最小、最大雷数
1150    // 输入矩阵必须是非空的,且行列数必须匹配
1151    // 行数和列数至少为1
1152    let cells_num = matrixx_squeeze.len();
1153    if cells_num > ENUM_LIMIT {
1154        // 超出枚举极限长度异常
1155        return Err(cells_num);
1156    }
1157    let cells_num_total = combination_relationship
1158        .iter()
1159        .fold(0, |item, x| item + x.len());
1160    // cells_num_total指合并前的格子数
1161
1162    let mut flag_legal_board = true;
1163    let mut table_minenum: [Vec<usize>; 2] = [
1164        (0..cells_num_total + 1).collect::<Vec<usize>>(),
1165        vec![0; cells_num_total + 1],
1166    ];
1167    let (cell_to_equation_map, equation_to_cell_map) = cal_cell_and_equation_map(&matrixA_squeeze);
1168    // 计算两个映射表以减少复杂度
1169    // println!("cell_to_equation_map = {:?}; equation_to_cell_map = {:?}", cell_to_equation_map, equation_to_cell_map);
1170
1171    let mut table_cell_minenum: Vec<Vec<usize>> = vec![vec![0; cells_num]; cells_num_total + 1];
1172
1173    // println!("{:?}", matrixA_squeeze);
1174    cal_table_minenum_recursion_step(
1175        0,
1176        1,
1177        &mut table_minenum,
1178        &mut table_cell_minenum,
1179        &matrixA_squeeze,
1180        &matrix_b,
1181        &mut matrix_b.clone(),
1182        &combination_relationship,
1183        &cell_to_equation_map,
1184        &equation_to_cell_map,
1185        &mut (vec![0; cells_num]),
1186    )?;
1187    // println!("table_cell_minenum{:?}", table_cell_minenum);
1188    // println!("table_minenum{:?}", table_minenum);
1189    while table_minenum[1][0] == 0 {
1190        table_minenum[0].remove(0);
1191        table_minenum[1].remove(0);
1192        table_cell_minenum.remove(0);
1193        if table_cell_minenum.is_empty() {
1194            flag_legal_board = false;
1195            break;
1196        }
1197    }
1198    if flag_legal_board {
1199        while table_minenum[1][table_cell_minenum.len() - 1] == 0 {
1200            table_minenum[0].pop();
1201            table_minenum[1].pop();
1202            table_cell_minenum.pop();
1203        }
1204    }
1205    if flag_legal_board {
1206        Ok((table_minenum, table_cell_minenum))
1207    } else {
1208        return Err(1);
1209    }
1210}
1211
1212// pub fn cal_table_minenum_enum(
1213//     matrixA_squeeze: &Vec<Vec<i32>>,
1214//     matrixx_squeeze: &Vec<(usize, usize)>,
1215//     matrix_b: &Vec<i32>,
1216//     combination_relationship: &Vec<Vec<usize>>,
1217// ) -> Result<([Vec<usize>; 2], Vec<Vec<usize>>), usize> {
1218//     // 拟弃用,用cal_table_minenum_recursion代替
1219//     // 枚举并统计,得到雷数分布表和每格是雷情况数表
1220//     let mut table_minenum: [Vec<usize>; 2] = [vec![], vec![]];
1221//     // 雷数分布表表:记录了每块(不包括内部块)每种总雷数下的是雷总情况数
1222//     // 例如:[[17, 18, 19, 20, 21, 22, 23, 24], [48, 2144, 16872, 49568, 68975, 48960, 16608, 2046]]
1223//     let mut table_cell_minenum: Vec<Vec<usize>> = vec![];
1224//     // 每格是雷情况数表:记录了每块每格(或者地位等同的复合格)、每种总雷数下的是雷情况数
1225//     if matrixx_squeeze.len() > 45 {
1226//         // 超出枚举极限长度
1227//         return Err(0);
1228//     }
1229//     let enum_comb_table: Vec<Vec<u8>> = enum_comb(&matrixA_squeeze, &matrixx_squeeze, &matrix_b);
1230//     if enum_comb_table.len() == 0 {
1231//         // 无解局面
1232//         return Err(1);
1233//     }
1234//     for s in enum_comb_table.clone() {
1235//         // println!("s: {:?}", s);
1236//         let s_sum = s.iter().sum::<u8>();
1237//         let mut si_num = 1; // 由于enum_comb_table中的格子每一个都代表了与其地位等同的所有格子,由此的情况数
1238//         for s_i in 0..s.len() {
1239//             si_num *= C_query(combination_relationship[s_i].len(), s[s_i]);
1240//         }
1241//         let fs = table_minenum[0]
1242//             .clone()
1243//             .iter()
1244//             .position(|x| *x == s_sum.into());
1245//         match fs {
1246//             None => {
1247//                 table_minenum[0].push(s_sum.into());
1248//                 table_minenum[1].push(si_num.into());
1249//                 let mut ss = vec![];
1250//                 for c in 0..s.len() {
1251//                     if s[c] == 0 {
1252//                         ss.push(0);
1253//                     } else {
1254//                         let mut sss = 1;
1255//                         for d in 0..s.len() {
1256//                             if c != d {
1257//                                 sss *= C_query(combination_relationship[d].len(), s[d]);
1258//                                 // println!("comb_relp_s = {:?}", comb_relp_s);
1259//                                 // println!("sss = {:?}", sss);
1260//                             } else {
1261//                                 sss *= C_query(combination_relationship[d].len() - 1, s[d] - 1);
1262//                             }
1263//                         }
1264//                         ss.push(sss as usize);
1265//                     }
1266//                 }
1267//                 table_cell_minenum.push(ss);
1268//             }
1269//             _ => {
1270//                 table_minenum[1][fs.unwrap()] += si_num as usize;
1271//                 for c in 0..s.len() {
1272//                     if s[c] == 0 {
1273//                         continue;
1274//                     } else {
1275//                         let mut sss = 1;
1276//                         for d in 0..s.len() {
1277//                             if c != d {
1278//                                 sss *= C_query(combination_relationship[d].len(), s[d]);
1279//                                 // println!("comb_relp_s=={:?}", comb_relp_s);
1280//                                 // println!("s=={:?}", s);
1281//                             } else {
1282//                                 sss *= C_query(combination_relationship[d].len() - 1, s[d] - 1);
1283//                             }
1284//                         }
1285//                         table_cell_minenum[fs.unwrap()][c] += sss as usize;
1286//                     }
1287//                 }
1288//             }
1289//         }
1290//     }
1291
1292//     Ok((table_minenum, table_cell_minenum))
1293// }
1294
1295/// 用几种模板,检测实际局面中是否有明显的死猜的结构。  
1296/// - 使用模板包括:工型、回型、器型。  
1297/// - 注意:对于一个局面,即使该检测返回true,也不能判断其必然是无猜的局面。想要真正判断一个局面无猜,请使用[is_solvable](#is_solvable)  
1298/// - 注意:局面至少大于4*4。
1299pub fn unsolvable_structure(BoardCheck: &Vec<Vec<i32>>) -> bool {
1300    let row = BoardCheck.len();
1301    let column = BoardCheck[0].len();
1302    let mut Board = vec![vec![0; column]; row];
1303    for i in 0..row {
1304        for j in 0..column {
1305            if BoardCheck[i][j] == -1 {
1306                Board[i][j] = -1;
1307            }
1308        }
1309    }
1310    for i in 0..row - 2 {
1311        // 检查左右两侧的工
1312        if i < row - 3 {
1313            if Board[i][0] == -1
1314                && Board[i][1] == -1
1315                && Board[i + 3][0] == -1
1316                && Board[i + 3][1] == -1
1317                && Board[i + 1][0] + Board[i + 2][0] == -1
1318                || Board[i][column - 1] == -1
1319                    && Board[i][column - 2] == -1
1320                    && Board[i + 3][column - 1] == -1
1321                    && Board[i + 3][column - 2] == -1
1322                    && Board[i + 1][column - 1] + Board[i + 2][column - 1] == -1
1323            {
1324                return true;
1325            }
1326        }
1327        if Board[i][2] == -1
1328            && Board[i + 1][2] == -1
1329            && Board[i + 2][2] == -1
1330            && Board[i + 1][0] + Board[i + 1][1] == -1
1331            || Board[i][column - 3] == -1
1332                && Board[i + 1][column - 3] == -1
1333                && Board[i + 2][column - 3] == -1
1334                && Board[i + 1][column - 1] + Board[i + 1][column - 2] == -1
1335            || Board[i][0] == -1
1336                && Board[i][1] == -1
1337                && Board[i + 1][1] == -1
1338                && Board[i + 2][1] == -1
1339                && Board[i + 2][0] == -1
1340                && Board[i + 1][0] == 0
1341            || Board[i][column - 1] == -1
1342                && Board[i][column - 2] == -1
1343                && Board[i + 1][column - 2] == -1
1344                && Board[i + 2][column - 2] == -1
1345                && Board[i + 2][column - 1] == -1
1346                && Board[i + 1][column - 1] == 0
1347        {
1348            return true;
1349        }
1350        if i < row - 3 {
1351            if Board[i][2] == -1
1352                && Board[i + 3][2] == -1
1353                && Board[i + 1][0] + Board[i + 1][1] == -1
1354                && Board[i + 1][1] + Board[i + 2][1] == -1
1355                && Board[i + 2][1] + Board[i + 2][0] == -1
1356                || Board[i][column - 3] == -1
1357                    && Board[i + 3][column - 3] == -1
1358                    && Board[i + 1][column - 1] + Board[i + 1][column - 2] == -1
1359                    && Board[i + 1][column - 2] + Board[i + 2][column - 2] == -1
1360                    && Board[i + 2][column - 2] + Board[i + 2][column - 1] == -1
1361            {
1362                return true;
1363            }
1364        }
1365    }
1366    for j in 0..column - 2 {
1367        // 检查上下两侧
1368        if j < column - 3 {
1369            if Board[0][j] == -1
1370                && Board[1][j] == -1
1371                && Board[0][j + 3] == -1
1372                && Board[1][j + 3] == -1
1373                && Board[0][j + 1] + Board[0][j + 2] == -1
1374                || Board[row - 1][j] == -1
1375                    && Board[row - 2][j] == -1
1376                    && Board[row - 1][j + 3] == -1
1377                    && Board[row - 2][j + 3] == -1
1378                    && Board[row - 1][j + 1] + Board[row - 1][j + 2] == -1
1379            {
1380                return true;
1381            }
1382        }
1383        if Board[2][j] == -1
1384            && Board[2][j + 1] == -1
1385            && Board[2][j + 2] == -1
1386            && Board[0][j + 1] + Board[1][j + 1] == -1
1387            || Board[row - 3][j] == -1
1388                && Board[row - 3][j + 1] == -1
1389                && Board[row - 3][j + 2] == -1
1390                && Board[row - 1][j + 1] + Board[row - 2][j + 1] == -1
1391            || Board[0][j] == -1
1392                && Board[1][j] == -1
1393                && Board[1][j + 1] == -1
1394                && Board[1][j + 2] == -1
1395                && Board[0][j + 2] == -1
1396                && Board[0][j + 1] == 0
1397            || Board[row - 1][j] == -1
1398                && Board[row - 2][j] == -1
1399                && Board[row - 2][j + 1] == -1
1400                && Board[row - 2][j + 2] == -1
1401                && Board[row - 1][j + 2] == -1
1402                && Board[row - 1][j + 1] == 0
1403        {
1404            return true;
1405        }
1406        if j < column - 3 {
1407            if Board[2][j] == -1
1408                && Board[2][j + 3] == -1
1409                && Board[0][j + 1] + Board[1][j + 1] == -1
1410                && Board[1][j + 1] + Board[1][j + 2] == -1
1411                && Board[1][j + 2] + Board[0][j + 2] == -1
1412                || Board[row - 3][j] == -1
1413                    && Board[row - 3][j + 3] == -1
1414                    && Board[row - 1][j + 1] + Board[row - 2][j + 1] == -1
1415                    && Board[row - 2][j + 1] + Board[row - 2][j + 2] == -1
1416                    && Board[row - 2][j + 2] + Board[row - 1][j + 2] == -1
1417            {
1418                return true;
1419            }
1420        }
1421    }
1422    if Board[0][2] == -1 && Board[1][2] == -1 && Board[0][0] + Board[0][1] == -1
1423        || Board[2][0] == -1 && Board[2][1] == -1 && Board[0][0] + Board[1][0] == -1
1424        || Board[0][column - 3] == -1
1425            && Board[1][column - 3] == -1
1426            && Board[0][column - 1] + Board[0][column - 2] == -1
1427        || Board[2][column - 1] == -1
1428            && Board[2][column - 2] == -1
1429            && Board[0][column - 1] + Board[1][column - 1] == -1
1430        || Board[row - 1][2] == -1
1431            && Board[row - 2][2] == -1
1432            && Board[row - 1][0] + Board[row - 1][1] == -1
1433        || Board[row - 3][0] == -1
1434            && Board[row - 3][1] == -1
1435            && Board[row - 1][0] + Board[row - 2][0] == -1
1436        || Board[row - 1][column - 3] == -1
1437            && Board[row - 2][column - 3] == -1
1438            && Board[row - 1][column - 1] + Board[row - 1][column - 2] == -1
1439        || Board[row - 3][column - 1] == -1
1440            && Board[row - 3][column - 2] == -1
1441            && Board[row - 1][column - 1] + Board[row - 2][column - 1] == -1
1442        || Board[0][1] + Board[1][1] + Board[1][0] == -3 && Board[0][0] == 0
1443        || Board[0][column - 2] + Board[1][column - 2] + Board[1][column - 1] == -3
1444            && Board[0][column - 1] == 0
1445        || Board[row - 1][column - 2] + Board[row - 2][column - 2] + Board[row - 2][column - 1]
1446            == -3
1447            && Board[row - 1][column - 1] == 0
1448        || Board[row - 1][1] + Board[row - 2][1] + Board[row - 2][0] == -3 && Board[row - 1][0] == 0
1449        || Board[2][2] == -1 && Board[0][1] + Board[1][1] == -1 && Board[1][0] + Board[1][1] == -1
1450        || Board[row - 3][2] == -1
1451            && Board[row - 1][1] + Board[row - 2][1] == -1
1452            && Board[row - 2][0] + Board[row - 2][1] == -1
1453        || Board[row - 3][column - 3] == -1
1454            && Board[row - 1][column - 2] + Board[row - 2][column - 2] == -1
1455            && Board[row - 2][column - 1] + Board[row - 2][column - 2] == -1
1456        || Board[2][column - 3] == -1
1457            && Board[0][column - 2] + Board[1][column - 2] == -1
1458            && Board[1][column - 1] + Board[1][column - 2] == -1
1459    {
1460        //检查四个角
1461        return true;
1462    }
1463    for i in 0..row - 2 {
1464        // 找中间的工、回、器形结构
1465        for j in 0..column - 2 {
1466            if j < column - 3 {
1467                if Board[i][j] == -1
1468                    && Board[i + 1][j] == -1
1469                    && Board[i + 2][j] == -1
1470                    && Board[i][j + 3] == -1
1471                    && Board[i + 1][j + 3] == -1
1472                    && Board[i + 2][j + 3] == -1
1473                    && Board[i + 1][j + 1] + Board[i + 1][j + 2] == -1
1474                {
1475                    return true;
1476                }
1477            }
1478            if i < row - 3 {
1479                if Board[i][j] == -1
1480                    && Board[i][j + 1] == -1
1481                    && Board[i][j + 2] == -1
1482                    && Board[i + 3][j] == -1
1483                    && Board[i + 3][j + 1] == -1
1484                    && Board[i + 3][j + 2] == -1
1485                    && Board[i + 1][j + 1] + Board[i + 2][j + 1] == -1
1486                {
1487                    return true;
1488                }
1489            }
1490            if Board[i][j] == -1
1491                && Board[i + 1][j] == -1
1492                && Board[i + 2][j] == -1
1493                && Board[i][j + 1] == -1
1494                && Board[i + 2][j + 1] == -1
1495                && Board[i][j + 2] == -1
1496                && Board[i + 1][j + 2] == -1
1497                && Board[i + 2][j + 2] == -1
1498                && Board[i + 1][j + 1] == 0
1499            {
1500                return true;
1501            }
1502            if j < column - 3 && i < row - 3 {
1503                if Board[i][j] == -1
1504                    && Board[i + 3][j] == -1
1505                    && Board[i][j + 3] == -1
1506                    && Board[i + 3][j + 3] == -1
1507                    && Board[i + 1][j + 1] + Board[i + 2][j + 1] == -1
1508                    && Board[i + 1][j + 1] + Board[i + 1][j + 2] == -1
1509                    && Board[i + 2][j + 1] + Board[i + 2][j + 2] == -1
1510                {
1511                    return true;
1512                }
1513            }
1514        }
1515    }
1516    false
1517}
1518
1519// 专用于高级局面的3BV快速计算
1520pub fn cal_bbbv_exp(Board: &Vec<Vec<i32>>) -> usize {
1521    let mut board = Board.clone();
1522    let mut op_id = 0;
1523    let mut op_list = [false; 200];
1524    let mut bv = 0;
1525    for x in 0..16 {
1526        for y in 0..30 {
1527            if board[x][y] > 0 {
1528                board[x][y] = 1000000;
1529                bv += 1;
1530            } else if board[x][y] == 0 {
1531                let mut min_op_id = 1000;
1532                let mut flag_op = false; // 该空周围有无空的标志位
1533                if x >= 1 {
1534                    for j in max(1, y) - 1..min(30, y + 2) {
1535                        if board[x - 1][j] > 999999 {
1536                            board[x - 1][j] = 1;
1537                            bv -= 1;
1538                        } else if Board[x - 1][j] == 0 {
1539                            if board[x - 1][j] < min_op_id {
1540                                if flag_op {
1541                                    op_list[min_op_id as usize] = false;
1542                                } else {
1543                                    flag_op = true;
1544                                }
1545                                min_op_id = board[x - 1][j];
1546                            }
1547                        }
1548                    }
1549                }
1550                if y >= 1 {
1551                    if board[x][y - 1] > 999999 {
1552                        board[x][y - 1] = 1;
1553                        bv -= 1;
1554                    } else if Board[x][y - 1] == 0 {
1555                        if board[x][y - 1] < min_op_id {
1556                            if flag_op {
1557                                op_list[min_op_id as usize] = false;
1558                            } else {
1559                                flag_op = true;
1560                            }
1561                            min_op_id = board[x][y - 1];
1562                        }
1563                    }
1564                }
1565                if !flag_op {
1566                    op_id += 1;
1567                    op_list[op_id as usize] = true;
1568                }
1569            }
1570        }
1571    }
1572    for x in (0..16).rev() {
1573        for y in (0..30).rev() {
1574            if board[x][y] == 0 {
1575                if x <= 14 {
1576                    for j in max(1, y) - 1..min(30, y + 2) {
1577                        if board[x + 1][j] > 999999 {
1578                            board[x + 1][j] = 1;
1579                            bv -= 1;
1580                        } else if Board[x + 1][j] == 0 {
1581                            if board[x + 1][j] < board[x][y] {
1582                                op_list[board[x][y] as usize] = false;
1583                                board[x][y] = board[x + 1][j];
1584                            }
1585                        }
1586                    }
1587                }
1588                if y <= 28 {
1589                    if board[x][y + 1] > 999999 {
1590                        board[x][y + 1] = 1;
1591                        bv -= 1;
1592                    } else if Board[x][y + 1] == 0 {
1593                        if board[x][y + 1] < board[x][y] {
1594                            op_list[board[x][y] as usize] = false;
1595                            board[x][y] = board[x][y + 1];
1596                        }
1597                    }
1598                }
1599            }
1600        }
1601    }
1602    for i in 0..op_id + 1 {
1603        if op_list[i] {
1604            bv += 1;
1605        }
1606    }
1607    bv
1608}
1609
1610// 把局面合法化:只能合法化简单的情况,不能应付所有的情况!因为检查一个局面是否合法也是NP难的
1611// 配合局面光学识别算法
1612// 局面中标记的标准是10为待判的雷,1到8,没有11、12
1613pub fn legalize_board(board: &mut Vec<Vec<i32>>) {
1614    let row = board.len();
1615    let column = board[0].len();
1616    for x in 0..row {
1617        for y in 0..column {
1618            if board[x][y] <= -1 || board[x][y] >= 13 || board[x][y] == 9 {
1619                // 把局面中明显未定义的数字改成未打开
1620                board[x][y] = 10;
1621            } else if board[x][y] >= 1 && board[x][y] <= 8 {
1622                let mut minenum_limit = 0;
1623                for i in max(1, x) - 1..min(row, x + 2) {
1624                    for j in max(1, y) - 1..min(column, y + 2) {
1625                        if board[i][j] == 10 || board[i][j] == 11 {
1626                            // 局面中的数字不能大于周围的未知格数
1627                            minenum_limit += 1;
1628                        }
1629                    }
1630                }
1631                board[x][y] = min(board[x][y], minenum_limit);
1632            }
1633        }
1634    }
1635}
1636
1637// 重新分块矩阵
1638// 这些矩阵必须非空、没有空的块、没有b=0的情况
1639pub fn chunk_matrixes(
1640    matrix_as: &mut Vec<Vec<Vec<i32>>>,
1641    matrix_xs: &mut Vec<Vec<(usize, usize)>>,
1642    matrix_bs: &mut Vec<Vec<i32>>,
1643) {
1644    let block_num = matrix_bs.len();
1645    let mut aas = vec![];
1646    let mut xxs = vec![];
1647    let mut bbs = vec![];
1648    for _ in 0..block_num {
1649        let aa = matrix_as.pop().unwrap();
1650        let xx = matrix_xs.pop().unwrap();
1651        let bb = matrix_bs.pop().unwrap();
1652        let (mut a_, mut x_, mut b_) = chunk_matrix(aa, xx, bb);
1653        aas.append(&mut a_);
1654        xxs.append(&mut x_);
1655        bbs.append(&mut b_);
1656    }
1657    *matrix_as = aas;
1658    *matrix_xs = xxs;
1659    *matrix_bs = bbs;
1660}
1661
1662// 重新分块一个矩阵
1663// 这些矩阵必须非空、没有空的块、没有b=0的情况
1664pub fn chunk_matrix(
1665    mut matrix_a: Vec<Vec<i32>>,
1666    mut matrix_x: Vec<(usize, usize)>,
1667    mut matrix_b: Vec<i32>,
1668) -> (Vec<Vec<Vec<i32>>>, Vec<Vec<(usize, usize)>>, Vec<Vec<i32>>) {
1669    let mut block_id = 0;
1670    let mut matrix_as = vec![];
1671    let mut matrix_xs = vec![];
1672    let mut matrix_bs = vec![];
1673
1674    loop {
1675        let row_num = matrix_a.len();
1676        let column_num = matrix_a[0].len();
1677        let mut current_rows_bool = vec![false; row_num];
1678        let mut current_columns_bool = vec![false; column_num];
1679        current_columns_bool[0] = true;
1680        let mut column_buffer = vec![0];
1681        loop {
1682            let mut row_buffer = vec![];
1683            if column_buffer.is_empty() {
1684                break;
1685            }
1686            for i in &column_buffer {
1687                for idr in 0..matrix_a.len() {
1688                    if matrix_a[idr][*i] >= 1 && !current_rows_bool[idr] {
1689                        row_buffer.push(idr);
1690                        current_rows_bool[idr] = true;
1691                    }
1692                }
1693            }
1694            column_buffer.clear();
1695            if row_buffer.is_empty() {
1696                break;
1697            }
1698            for i in row_buffer {
1699                for (idc, &c) in matrix_a[i].iter().enumerate() {
1700                    if c >= 1 && !current_columns_bool[idc] {
1701                        column_buffer.push(idc);
1702                        current_columns_bool[idc] = true;
1703                    }
1704                }
1705            }
1706        }
1707        let mut current_rows = vec![];
1708        let mut current_columns = vec![];
1709        for (idx, &x) in current_columns_bool.iter().enumerate() {
1710            if x {
1711                current_columns.push(idx)
1712            }
1713        }
1714        for (idx, &x) in current_rows_bool.iter().enumerate() {
1715            if x {
1716                current_rows.push(idx)
1717            }
1718        }
1719        current_rows.sort_by(|a, b| b.cmp(a));
1720        current_rows.dedup();
1721        current_columns.sort_by(|a, b| b.cmp(a));
1722        current_columns.dedup();
1723        matrix_as.push(vec![vec![0; current_columns.len()]; current_rows.len()]);
1724        matrix_bs.push(vec![0; current_rows.len()]);
1725        matrix_xs.push(vec![(0, 0); current_columns.len()]);
1726        for (idx, x) in current_rows.iter().enumerate() {
1727            for (idy, y) in current_columns.iter().enumerate() {
1728                matrix_as[block_id][idx][idy] = matrix_a[*x][*y];
1729            }
1730        }
1731        for (idx, x) in current_rows.iter().enumerate() {
1732            matrix_bs[block_id][idx] = matrix_b[*x];
1733        }
1734        for (idy, y) in current_columns.iter().enumerate() {
1735            matrix_xs[block_id][idy] = matrix_x[*y];
1736        }
1737        for i in current_rows {
1738            matrix_a.remove(i);
1739            matrix_b.remove(i);
1740        }
1741        for j in current_columns {
1742            for k in 0..matrix_a.len() {
1743                matrix_a[k].remove(j);
1744            }
1745            matrix_x.remove(j);
1746        }
1747
1748        if matrix_b.is_empty() {
1749            break;
1750        }
1751        block_id += 1;
1752    }
1753    (matrix_as, matrix_xs, matrix_bs)
1754}
1755
1756#[test]
1757fn chunk_matrix_works() {
1758    let mut a = vec![
1759        vec![1, 1, 0, 0],
1760        vec![0, 0, 1, 1],
1761        vec![0, 1, 0, 0],
1762        vec![0, 0, 0, 1],
1763    ];
1764    let mut x = vec![(1, 2), (3, 4), (5, 6), (7, 8)];
1765    let mut b = vec![1, 2, 3, 4];
1766    let (aa, xx, bb) = chunk_matrix(a, x, b);
1767    println!("{:?}", xx);
1768}
1769
1770// 找局面中间的格子的所在块的任意一个边界的格子。(可能不严格)
1771// 与弱无猜、准无猜有关。涉及判断是否为“必要的猜雷”。
1772// 点中间时,需要判断整块无猜以后,才能判定是合理的猜雷。
1773// xy处必须是10。第二个返回值true代表xy在边界,false代表在内部。
1774pub fn find_a_border_cell(
1775    board_of_game: &Vec<Vec<i32>>,
1776    xy: &(usize, usize),
1777) -> (Option<(usize, usize)>, bool) {
1778    let row = board_of_game.len();
1779    let column = board_of_game[0].len();
1780    for m in max(1, xy.0) - 1..min(row, xy.0 + 2) {
1781        for n in max(1, xy.1) - 1..min(column, xy.1 + 2) {
1782            if board_of_game[m][n] < 10 {
1783                return (Some(*xy), true);
1784            }
1785        }
1786    }
1787    let mut board_of_game_clone = board_of_game.clone();
1788    board_of_game_clone[xy.0][xy.1] = 100;
1789    let mut buffer = vec![(xy.0, xy.1)];
1790    while let Some(top) = buffer.pop() {
1791        let (i, j) = top;
1792        for m in max(1, i) - 1..min(row, i + 2) {
1793            for n in max(1, j) - 1..min(column, j + 2) {
1794                if (i != m || j != n) && board_of_game_clone[m][n] == 10 {
1795                    for mm in max(1, m) - 1..min(row, m + 2) {
1796                        for nn in max(1, n) - 1..min(column, n + 2) {
1797                            if board_of_game_clone[mm][nn] < 10 {
1798                                return (Some((m, n)), false);
1799                            }
1800                        }
1801                    }
1802                    buffer.push((m, n));
1803                    board_of_game_clone[m][n] = 100;
1804                }
1805            }
1806        }
1807    }
1808    (None, false)
1809}
1810
1811/// 是局部最好的双击返回真,否则为假。方法是向四周试探一个位置,好的双击应该不能打开更多的格子。
1812/// - 不检查,但要保证pos位置处一定是合法、有效的双击,否则没意义。
1813/// - board_of_game必须是没有标错的雷的,如果分析录像,必须不是尸体。
1814pub fn is_good_chording(board_of_game: &Vec<Vec<i32>>, pos: (usize, usize)) -> bool {
1815    let row = board_of_game.len();
1816    let column = board_of_game[0].len();
1817    let mid_num = surround_cell_num(board_of_game, pos);
1818    if pos.0 > 0 {
1819        if mid_num < surround_cell_num(board_of_game, (pos.0 - 1, pos.1)) {
1820            return false;
1821        }
1822    }
1823    if pos.1 > 0 {
1824        if mid_num < surround_cell_num(board_of_game, (pos.0, pos.1 - 1)) {
1825            return false;
1826        }
1827    }
1828    if pos.0 + 1 < row {
1829        if mid_num < surround_cell_num(board_of_game, (pos.0 + 1, pos.1)) {
1830            return false;
1831        }
1832    }
1833    if pos.1 < column + 1 {
1834        if mid_num < surround_cell_num(board_of_game, (pos.0, pos.1 + 1)) {
1835            return false;
1836        }
1837    }
1838    return mid_num > 0;
1839}
1840
1841// (双击位置)周围的格子(10)数,不合法则返回-1。
1842// - board_of_game必须是没有标错的雷的,如果分析录像,必须不是尸体。
1843fn surround_cell_num(board_of_game: &Vec<Vec<i32>>, pos: (usize, usize)) -> i8 {
1844    let row = board_of_game.len();
1845    let column = board_of_game[0].len();
1846    if board_of_game[pos.0][pos.1] > 8 || board_of_game[pos.0][pos.1] < 1 {
1847        return -1;
1848    }
1849    let mut flag_num = 0;
1850    let mut num = 0;
1851    for m in max(1, pos.0) - 1..min(row, pos.0 + 2) {
1852        for n in max(1, pos.1) - 1..min(column, pos.1 + 2) {
1853            if board_of_game[m][n] == 10 {
1854                num += 1;
1855            } else if board_of_game[m][n] == 11 {
1856                flag_num += 1;
1857            }
1858        }
1859    }
1860    if board_of_game[pos.0][pos.1] as i8 == flag_num {
1861        return num;
1862    } else {
1863        return -1;
1864    }
1865}
1866
1867/// 算数字。局面上只有0和-1时,计算其他的数字。不具备幂等性!!!
1868pub fn cal_board_numbers(board: &mut Vec<Vec<i32>>) {
1869    let height = board.len();
1870    let width = board[0].len();
1871    for x in 0..height {
1872        for y in 0..width {
1873            if board[x][y] == -1 {
1874                for j in max(1, x) - 1..min(height, x + 2) {
1875                    for k in max(1, y) - 1..min(width, y + 2) {
1876                        if board[j][k] >= 0 {
1877                            board[j][k] += 1;
1878                        }
1879                    }
1880                }
1881            }
1882        }
1883    }
1884}