/********************************* * * ぱずぷれで作成したtxtファイルの読み込み(回答途中) * # : 黒 * + : 白 * ・ : 未定 * 1,2 : [12] * * *******************************/ public void readPartwayTapaTxt(string readfile_path) { List <string> line_list = new List <string>(); using (StreamReader sr = new StreamReader( readfile_path, Encoding.GetEncoding("Shift_JIS"))) { string line = ""; while ((line = sr.ReadLine()) != null) { line_list.Add(line); } } if (line_list[0] != "pzprv3" || line_list[1] != "tapa") { Problem.is_correct_txtformat = false; return; } Tapa.MAX_BOARD_ROW = Convert.ToInt32(line_list[2]); // 3行目に行数 Tapa.MAX_BOARD_COL = Convert.ToInt32(line_list[3]); // 4行目に列数 Tapa.BOX_SUM = Tapa.MAX_BOARD_ROW * Tapa.MAX_BOARD_COL; // 盤面生成 Tapa.resetBoard(); for (int i = 1; i <= Tapa.MAX_BOARD_ROW; i++) { string wk_str = line_list[i + 3].Replace(",", ""); // 数字マスの区切りを削除 for (int j = 1; j <= Tapa.MAX_BOARD_COL; j++) { int pt = wk_str.IndexOf(' '); if (wk_str.Substring(0, pt) == "#") { Tapa.box[i][j].connecting_color = Box.BLACK; } else if (wk_str.Substring(0, pt) == "+") { Tapa.box[i][j].connecting_color = Box.WHITE; } else if (char.IsDigit(wk_str, 0)) { Box tmp_box = Tapa.box[i][j]; tmp_box.hasNum = true; tmp_box.boxNum = int.Parse(wk_str.Substring(0, pt)); Tapa.not_deployedbox_coord_list.Remove(tmp_box.coord); Tapa.numbox_coord_list.Add(tmp_box.coord); } else { } wk_str = wk_str.Substring(pt + 1); } } // 数字に対応したidを格納 PatternAroundNumBox.preparePatternArroundNumBox(); }
/********************************* * * 問題を生成するプログラムを呼び出す。 * 引数 * pattern : 呼び出すプログラムのid * 0 >> 数字を追加して問題生成する * 1 >> 数字を削除して問題生成する * 2 >> 数字を追加したあと冗長な数字を削除して問題生成する * * *******************************/ private void generateTapaPrblem(int pattern) { // 数字マスの座標とその数字を関連付けたハッシュ Dictionary <Coordinates, int> boxnumber_in_whitebox_coord_dict = new Dictionary <Coordinates, int>(); foreach (Coordinates tmp_co in Problem.can_be_number_whitebox_list) { boxnumber_in_whitebox_coord_dict[tmp_co] = Tapa.box[tmp_co.x][tmp_co.y].boxNum; } // (黒マスListなども含む)盤面の初期化 Tapa.resetBoard(); switch (pattern) { case 0: generateTapaProblemInAddNumBox(boxnumber_in_whitebox_coord_dict); break; case 1: // 数字マスを全て配置する Box.during_make_inputbord = true; foreach (KeyValuePair <Coordinates, int> pair in boxnumber_in_whitebox_coord_dict) { Box tmp_box = Tapa.box[pair.Key.x][pair.Key.y]; tmp_box.hasNum = true; // 数字を持ってるフラグをオンにする tmp_box.boxNum = boxnumber_in_whitebox_coord_dict[pair.Key]; // 選択した座標に数字を格納 Tapa.numbox_coord_list.Add(new Coordinates(pair.Key)); // 数字マスリストに追加 Tapa.not_deployedbox_coord_list.Remove(pair.Key); // 未定マスリストから除外 PatternAroundNumBox.preparePatternArroundNumBox(); // 数字に対応したidを格納 } Box.during_make_inputbord = false; generateTapaProblemInDeleteNumBox(); break; case 2: // 時間計測開始 System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); generateTapaProblemInAddNumBox(boxnumber_in_whitebox_coord_dict); //時間計測終了 sw.Stop(); Console.WriteLine("generateTapaProblemInAddNumBoxの総時間 >> " + sw.Elapsed + "(" + Problem.can_be_number_whitebox_list.Count + ")"); // 時間計測開始 System.Diagnostics.Stopwatch sw2 = System.Diagnostics.Stopwatch.StartNew(); generateTapaProblemInDeleteNumBox(); //時間計測終了 sw2.Stop(); Console.WriteLine("generateTapaProblemInDeleteNumBoxの総時間 >> " + sw2.Elapsed + "(" + Problem.can_be_number_whitebox_list.Count + ")"); break; } }
/********************************* * * 数字マスを設置する * * 引数 * num_dict : 盤面全ての数字マスとその値 * * *******************************/ private static void setNumBoxInBoard(Dictionary <Coordinates, int> num_dict) { foreach (KeyValuePair <Coordinates, int> pair in num_dict) { Box b = Tapa.box[pair.Key.x][pair.Key.y]; b.hasNum = true; // 数字を持ってるフラグをオンにする b.boxNum = pair.Value; // 選択した座標に数字を格納 b.id_list = PatternAroundNumBox.getPatternAroundNumBoxList(pair.Value); // 数字に対応したidを格納 Tapa.numbox_coord_list.Add(new Coordinates(pair.Key)); // 数字マスリストに追加 Tapa.not_deployedbox_coord_list.Remove(pair.Key); // 未定マスリストから除外 } }
public static void solveTapa(int solve_limit = -1) { is_over_solve_num = false; int process_limit = 3; while (true) { Tapa.was_change_board = false; // 数字マス周りのパターンを管理 PatternAroundNumBox.managePatternAroundNumBox(process_limit, solve_limit); if (Tapa.is_over_solve_num) { break; } // 伸び代のある黒マスから、黒マスが伸びないかを見て、可能なら実際に伸ばす。 Box.manageBlackBox(solve_limit); if (Tapa.is_over_solve_num) { break; } if (!was_change_board) { if (process_limit == 3) { if (Tapa.is_fast) { return; } // 高速モードなら手法の制限解除を行わない process_limit = 4; continue; // 解法を制限して回答が進まなければ制限解除 } return; // 解法の制限をなくしても問題が解けない or 回答終了 の場合終わり } if (process_limit == 4) { process_limit = 3; } // 解法の制限を解除して解が進んだ場合、改めて解法を制限する } }
/********************************* * * 未定マスを試し塗りしてバックトラックを行う * バックトラックの結果が一度でも正しければtrueを返す * 引数 * depth : バックトラックの深さ * * *******************************/ private bool doBackTrack(int depth) { StateSave save_point = new StateSave(); StateSave.saveNowState(save_point); bool ret_bool = false; // true:(途中)盤面が正しい // 深さをインクリメント depth++; // 黒マスと接している未定マスのリストを作成 List <Coordinates> adjacent_notdeployedbox_coord_list = new List <Coordinates>(); // 黒マスと接している未定マスがある時 foreach (Coordinates tmp_ndbox_coord in Tapa.not_deployedbox_coord_list) { if (Box.existBlackBoxAround(tmp_ndbox_coord)) { adjacent_notdeployedbox_coord_list.Add(tmp_ndbox_coord); } } // 未定マスはあるが、黒マスと接している未定マスがない時 if (adjacent_notdeployedbox_coord_list.Count == 0 && Tapa.not_deployedbox_coord_list.Count > 0) { foreach (Coordinates tmp_ndbox_coord in Tapa.not_deployedbox_coord_list) { adjacent_notdeployedbox_coord_list.Add(new Coordinates(tmp_ndbox_coord)); } } // 注目している未定マス領域に未定マスがただ1つ存在する時の処理 if (adjacent_notdeployedbox_coord_list.Count == 1) { for (int i = 0; i < 2; i++) { Coordinates tmp_ndbox_coord = adjacent_notdeployedbox_coord_list[0]; Tapa.box[tmp_ndbox_coord.x][tmp_ndbox_coord.y].Color = (i == 0) ? Box.BLACK : Box.WHITE; // 変化がなくなるまで処理を行う do { Tapa.was_change_board = false; PatternAroundNumBox.managePatternAroundNumBox(); Box.manageBlackBox(); } while (Tapa.was_change_board); // 処理中の未定マス群から色のついたマスを除外したリストを作成 List <Coordinates> arg_list = new List <Coordinates>(Tapa.not_deployedbox_coord_list); for (int j = arg_list.Count - 1; j >= 0; j--) { if (Tapa.box[arg_list[j].x][arg_list[j].y].Color != Box.NOCOLOR) { arg_list.RemoveAt(j); } } // 未定マスが存在すれば再起する if (arg_list.Count > 0) { ret_bool = ret_bool || doBackTrack(depth); } else // 未定マスが存在しない { if (Box.checkNotIsolationBlackBoxGroup() || Tapa.isCorrectAnswer()) // 盤面の一繋がりの黒マス群が孤立していないか { StateSave.saveNowState(BackTrack.correct_save_point); // 正しければ現在の盤面を保存 if (depth < min_depth) // 先読みした深さ(のうち小さい方)を記録 { min_depth = depth; tmp_ndbox_coord.printCoordinates(); Console.WriteLine(":min_depth >> " + min_depth); } ret_bool = true; } } // 元の状態に戻す StateSave.loadSavedState(save_point); // 黒マスで塗ったら正解の盤面が生成できなかった場合 if (!ret_bool) { // 未定マスの色を白にする Tapa.box[tmp_ndbox_coord.x][tmp_ndbox_coord.y].Color = Box.WHITE; StateSave.saveNowState(save_point); } } return(ret_bool); } // 注目している未定マス領域に未定マスが複数存在する時の処理 // 未定マスをたどる for (int i = 0; i < adjacent_notdeployedbox_coord_list.Count; i++) { Coordinates tmp_ndbox_coord = adjacent_notdeployedbox_coord_list[i]; // 未定マスの色を黒にする Tapa.box[tmp_ndbox_coord.x][tmp_ndbox_coord.y].Color = Box.BLACK; // 変化がなくなるまで処理を行う do { Tapa.was_change_board = false; PatternAroundNumBox.managePatternAroundNumBox(); Box.manageBlackBox(); } while (Tapa.was_change_board); // 処理中の未定マス群から色のついたマスを除外したリストを作成 List <Coordinates> arg_list = new List <Coordinates>(Tapa.not_deployedbox_coord_list); for (int j = arg_list.Count - 1; j >= 0; j--) { if (Tapa.box[arg_list[j].x][arg_list[j].y].Color != Box.NOCOLOR) { arg_list.RemoveAt(j); } } // 処理中の未定マス群に未定マスが残っていれば再起する if (arg_list.Count > 0) { ret_bool = ret_bool || doBackTrack(depth); } else // 処理中の未定マス群に未定マスが存在しない // 盤面に一繋がりの黒マス群が孤立していない or 盤面が正解 { if (Box.checkNotIsolationBlackBoxGroup() || Tapa.isCorrectAnswer()) { StateSave.saveNowState(BackTrack.correct_save_point); // 正しければ現在の盤面を保存 if (depth < min_depth) // 先読みした深さ(のうち小さい方)を記録 { min_depth = depth; tmp_ndbox_coord.printCoordinates(); Console.WriteLine(":min_depth >> " + min_depth); } ret_bool = true; } } // 元の状態に戻す StateSave.loadSavedState(save_point); // 黒マスで塗ったら正解の盤面が生成できなかった場合 if (!ret_bool) { // 未定マスの色を白にする Tapa.box[tmp_ndbox_coord.x][tmp_ndbox_coord.y].Color = Box.WHITE; StateSave.saveNowState(save_point); } } return(ret_bool); }
/********************************* * * 数字マスを追加して問題生成する。 * 引数: * boxnumber_in_whitebox_coord_dict : 数字マスの座標と数字の対応 * * *******************************/ private void generateTapaProblemInAddNumBox(Dictionary <Coordinates, int> boxnumber_in_whitebox_coord_dict) { do { // 時間計測開始 System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); // 埋める数字マスをランダムに選択 Coordinates adopting_boxnumber_coord = Problem.can_be_number_whitebox_list[ Problem.getRandomInt(0, Problem.can_be_number_whitebox_list.Count)]; Box tmp_box = Tapa.box[adopting_boxnumber_coord.x][adopting_boxnumber_coord.y]; // 選択した座標が既に配色済みの場合 if (tmp_box.Color != Box.NOCOLOR) { Problem.can_be_number_whitebox_list.Remove(adopting_boxnumber_coord); // 数字マスを格納できる座標リストから除外 // boxnumber_in_whitebox_coord_dict.Remove(adopting_boxnumber_coord); // ハッシュから除外 continue; } Box.during_make_inputbord = true; tmp_box.hasNum = true; // 数字を持ってるフラグをオンにする tmp_box.boxNum = boxnumber_in_whitebox_coord_dict[adopting_boxnumber_coord]; // 選択した座標に数字を格納 Tapa.numbox_coord_list.Add(new Coordinates(adopting_boxnumber_coord)); // 数字マスリストに追加 Tapa.not_deployedbox_coord_list.Remove(adopting_boxnumber_coord); // 未定マスリストから除外 tmp_box.id_list = PatternAroundNumBox.getPatternAroundNumBoxList(tmp_box.boxNum); // 数字に対応したidを格納 Problem.can_be_number_whitebox_list.Remove(adopting_boxnumber_coord); // 数字マスを格納できる座標リストから除外 boxnumber_in_whitebox_coord_dict.Remove(adopting_boxnumber_coord); // ハッシュから除外 Box.during_make_inputbord = false; Tapa.solveTapa(); //時間計測終了 sw.Stop(); adopting_boxnumber_coord.printCoordinates(); Console.WriteLine("の追加にかかった時間 >> " + sw.Elapsed + "(" + Problem.can_be_number_whitebox_list.Count + ")"); } while (Problem.can_be_number_whitebox_list.Count > 0); Console.WriteLine("数字マスランダム配置後"); Tapa.printBoard(); while (Tapa.not_deployedbox_coord_list.Count > 0) { Console.WriteLine("未定マスが存在!!!!"); // 白マス周りにある未定マスの数を格納 Dictionary <Coordinates, int> count_whitebox_dict = new Dictionary <Coordinates, int>(); foreach (Coordinates tmp_co in Tapa.not_deployedbox_coord_list) { List <Coordinates> whitebox_around_notdeployedbox_list = Box.getWhiteBoxCoordAround8(tmp_co); foreach (Coordinates tmp_whitebox_co in whitebox_around_notdeployedbox_list) { if (!count_whitebox_dict.ContainsKey(tmp_whitebox_co)) { count_whitebox_dict[tmp_whitebox_co] = 1; } else { count_whitebox_dict[tmp_whitebox_co]++; } } } int max = 0; Coordinates white2numbox = new Coordinates(); // 未定マスが最も多く周囲にある白マス foreach (KeyValuePair <Coordinates, int> pair in count_whitebox_dict) { if (pair.Value > max) { white2numbox = new Coordinates(pair.Key); } } Box.during_make_inputbord = true; Box tmp_box = Tapa.box[white2numbox.x][white2numbox.y]; tmp_box.hasNum = true; tmp_box.boxNum = boxnumber_in_whitebox_coord_dict[white2numbox]; // 数字を格納 Tapa.numbox_coord_list.Add(new Coordinates(white2numbox)); // 数字マスリストに追加 tmp_box.id_list = PatternAroundNumBox.getPatternAroundNumBoxList(tmp_box.boxNum); // 数字に対応したidを格納 Box.during_make_inputbord = false; Tapa.solveTapa(); if (Tapa.DEBUG) { Console.WriteLine("未定マス修正"); Tapa.printBoard(); } } // 数字ごとのidを元に戻す(回答中にid_listは減少) PatternAroundNumBox.preparePatternArroundNumBox(); // 盤面にある数字マス以外のマスを未定マスにする Tapa.numbox_coord_list.Clear(); // 数字マスの座標リスト Tapa.not_deployedbox_coord_list.Clear(); // 未定マスの座標リスト Tapa.isolation_notdeployedboxes_group_list.Clear(); // 一繋がりの未定マス群の座標リスト Tapa.edge_blackbox_coord_list.Clear(); // 伸び代のある黒マスの座標リスト Tapa.isolation_blackboxes_group_list.Clear(); // 一繋がりの黒マス群の座標リスト for (int i = 1; i <= Tapa.MAX_BOARD_ROW; i++) { for (int j = 1; j <= Tapa.MAX_BOARD_COL; j++) { Box tmp = Tapa.box[i][j]; if (!tmp.hasNum) { tmp.revision_color = Box.NOCOLOR; Tapa.not_deployedbox_coord_list.Add(new Coordinates(tmp.coord)); } else { Tapa.numbox_coord_list.Add(new Coordinates(tmp.coord)); } } } }