/********************************* * * 盤面の初期状態から始めて問題を解けるか判断する. * * True:解ける * * 引数 * partway : 途中盤面を保存したもの * result : 解答保存用 * * *******************************/ private static bool judgeCanSolveInitialBoard(StateSave partway, StateSave result) { // 数字マスの座標と数値のハッシュを作成 Dictionary <Coordinates, int> num_dict = new Dictionary <Coordinates, int>(); foreach (Coordinates num in Tapa.numbox_coord_list) { num_dict[num] = Tapa.box[num.x][num.y].boxNum; } // 盤面の初期化 Tapa.resetBoard(); // 数字マスの設置 Problem.setNumBoxInBoard(num_dict); // 回答する Tapa.solveTapa(); StateSave.saveNowState(result); if (Tapa.isCorrectAnswer()) { StateSave.loadSavedState(partway); return(true); } StateSave.loadSavedState(partway); return(false); }
/********************************* * * txtからヒントを生成 * * *******************************/ public static bool manageMakingHintFromTxt() { Problem p = new Problem(); p.readPartwayTapaTxt(Problem.prb_hintfile_path); StateSave partway = new StateSave(); StateSave result = new StateSave(); // 入力盤面の保存 StateSave.saveNowState(partway); // 初期盤面を解けないとき,途中まででも出力. if (!Problem.judgeCanSolveInitialBoard(partway, result)) { StateSave.loadSavedState(result); generateTapaHintText(Problem.ans_hintfile_path); return(false); } // 途中盤面が正しい場合,ヒントを与える. if (Problem.isCorrectMiddleBoard(partway)) { Tapa.solveTapa(Tapa.not_deployedbox_coord_list.Count - calcHintNum()); } // そうでない場合,間違った箇所を未定マスにする. else { Problem.modifyMistakeBoard(partway, result); } // ans_hintfile_path = @"C:\Users\Amano\OneDrive\zemi\ans_tapa.txt"; // ヒントtxtはexeファイルと同じディレクトリ generateTapaHintText(Problem.ans_hintfile_path); return(true); }
/********************************* * * 切断点のみの団子マスに接している黒マス群のうち * 最も短い黒マス群とそれに接している切断点を削除する * * *******************************/ private static void removeCutDumpling() { // 団子マス4つの保存用 List <Coordinates> dump_coord; // 団子マスの座標を取得、団子マスがなければ終わり while ((dump_coord = Box.getDumpCoord()) != null) { // 切断点に接している切断点以外の黒マス保存用 // Key : 切断点に接している黒マス // Value : 切断点 Dictionary <Coordinates, Coordinates> adj_dict = new Dictionary <Coordinates, Coordinates>(); foreach (Coordinates co in dump_coord) { // 切断点が接している黒マス座標を取得 List <Coordinates> tmp_adj_coord = Box.getWhatColorBoxCoordListAround(co, Box.BLACK); // 接している黒マスのうち切断点の情報を除外 foreach (Coordinates tmp_co in dump_coord) { tmp_adj_coord.Remove(tmp_co); } adj_dict[tmp_adj_coord[0]] = co; } // 盤面編集用 StateSave edit = new StateSave(); StateSave.saveNowState(edit); // 盤面editの団子マスdump_coordを未定マスにする foreach (Coordinates co in dump_coord) { edit.saved_box[co.x][co.y].revision_color = Box.NOCOLOR; } // edit盤面を生成 // (一繋がりの黒マス群が複数生成される) StateSave.makeEditBoard(edit); // 黒マス群リストのうち、最少の黒マス群の参照を取得 List <Coordinates> min_bblist = Box.getMinIsoBlackBoxListRef(); // 切断点の黒マスの団子を黒く塗る foreach (Coordinates co in dump_coord) { edit.saved_box[co.x][co.y].revision_color = Box.BLACK; } // min_bblistとそれに接している切断点を未定マスにする foreach (Coordinates co in min_bblist) { edit.saved_box[co.x][co.y].revision_color = Box.NOCOLOR; if (!adj_dict.ContainsKey(co)) { continue; } edit.saved_box[adj_dict[co].x][adj_dict[co].y].revision_color = Box.NOCOLOR; } // edit盤面を生成 // (団子マスを1つ除外できた盤面) StateSave.makeEditBoard(edit); } }
/********************************* * * 未定マスを試し塗りしてバックトラックを行う * バックトラックの結果が一度でも正しければ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 generateTapaProblemInDeleteNumBox() { if (Tapa.DEBUG) { Console.WriteLine("ソート前"); Tapa.printCoordList(Problem.can_be_number_whitebox_list); } // 【削除する数字マスを選ぶ順番】 // ヒント数で降順にソート[固定] のみ //////// ヒント数が同数だったらidの数で昇順にソートしたデータを後ろから選ぶ Tapa.numbox_coord_list.Sort( delegate(Coordinates co1, Coordinates co2) { int hint1 = Tapa.box[co1.x][co1.y].min_hint; int hint2 = Tapa.box[co2.x][co2.y].min_hint; if (hint1 < hint2) { return(1); } else if (hint1 > hint2) { return(-1); } else { return(0); //int num1 = Tapa.box[co1.x][co1.y].id_list.Count; //int num2 = Tapa.box[co2.x][co2.y].id_list.Count; //if (num1 < num2) { return -1; } //else if (num1 > num2) { return 1; } //else return 0; } }); // 未削除の数字マスリストを現在残っている数字マスリストで更新 Problem.can_be_number_whitebox_list = new List <Coordinates>(Tapa.numbox_coord_list); if (Tapa.DEBUG) { Console.WriteLine("ソート後"); Tapa.printCoordList(Problem.can_be_number_whitebox_list); } // 削除対象のリストを後ろからチェック for (int i = Problem.can_be_number_whitebox_list.Count - 1; i >= 0; i--) { // 時間計測開始 System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew(); // 現在の状態を保存 StateSave save_point = new StateSave(); StateSave.saveNowState(save_point); // 削除判定の行われていない数字マスの内、 // [最少ヒント数で降順][パターン数で昇順]でソートしたリストの後ろから順に数字マスを選ぶ Coordinates deleting_numbox_coord = Problem.can_be_number_whitebox_list[i]; // 選択された数字マスを、未削除の数字マス座標リストから除外する。 Problem.can_be_number_whitebox_list.Remove(deleting_numbox_coord); Box deleting_box = Tapa.box[deleting_numbox_coord.x][deleting_numbox_coord.y]; // 浅いコピー // 選択した数字マスを未定マスにする deleting_box.revision_color = Box.NOCOLOR; // 選択した座標を未定マスにする deleting_box.hasNum = false; // 数字を持ってるフラグをオフにする Tapa.numbox_coord_list.Remove(deleting_numbox_coord); // 数字マスリストから除外 Tapa.not_deployedbox_coord_list.Add(new Coordinates(deleting_numbox_coord)); // 未定マスリストに追加 // 問題を解く Tapa.solveTapa(); // 解ならば復元後に、選択した数字マスをそのまま未定マスにする。 if (Tapa.isCorrectAnswer()) { StateSave.loadSavedState(save_point); // 選択した数字マスを未定マスにする Tapa.box[deleting_numbox_coord.x][deleting_numbox_coord.y].revision_color = Box.NOCOLOR; // 選択した座標を未定マスにする Tapa.box[deleting_numbox_coord.x][deleting_numbox_coord.y].hasNum = false; // 数字を持ってるフラグをオフにする Tapa.numbox_coord_list.Remove(deleting_numbox_coord); // 数字マスリストから除外 Tapa.not_deployedbox_coord_list.Add(new Coordinates(deleting_numbox_coord)); // 未定マスリストに追加 } else { // 解でないなら、盤面を復元するのみ。 StateSave.loadSavedState(save_point); } //時間計測終了 sw.Stop(); deleting_numbox_coord.printCoordinates(); Console.WriteLine("del >> " + sw.Elapsed + "(" + Problem.can_be_number_whitebox_list.Count + ")"); } }