#include "game_sudo.h" #include "ui_main.h" constexpr std::array ARR_DIFF = { "简单", "中间", "高级", "高级+" }; constexpr std::array TIP_LV0 = { "此处不能填入当前数字。", "请确保横、竖、九宫都没有重复数字。" }; constexpr std::array TIP_LV1 = { "眼花了吧,这里不能放!", "请看仔细一点!" "不要点歪了!" }; constexpr std::array TIP_LV2 = { "你真笨……", "傻了吧,这都不会玩!" "不要乱点!" }; constexpr Position POS_LT = { 20.0, 20.0 }; constexpr Float LENGTH_GRID = 60; constexpr Position SIZE_GRID = toLonger(LENGTH_GRID); constexpr Position SIZE_BOARD = SIZE_GRID * 9.0_f; constexpr Float WIDTH_GRID_LINE_0 = 4; constexpr Float WIDTH_GRID_LINE_1 = 2; constexpr float ORDER_BOARD = 0.5f; constexpr float ORDER_GRID_LINE_0 = Math::OffsetOrder(ORDER_BOARD, -1); constexpr float ORDER_GRID_LINE_1 = Math::OffsetOrder(ORDER_BOARD, -2); constexpr float ORDER_NUMBER = Math::OffsetOrder(ORDER_BOARD, -4); constexpr float ORDER_MOUSE = Math::OffsetOrder(ORDER_BOARD, -5); constexpr float ORDER_MOUSE_TEXT = Math::OffsetOrder(ORDER_BOARD, -6); GameSudo::GameSudo() : _diff{ 0 }, _curNumber{ 0 }, _gameState{ 0 }, _tStart{ 0 }, _countErr{ 0 } { g_system->SetWindowSize({ 800, 600 }); _coor = Coor::Create(); //{ // // 安卓 // Position offset = toPosition(g_system->GetWindowSize()) - toPosition(800, 600); // _coor->SetPosition(offset); // GetPanelRoot()->SetPosPixel(offset); //} // 背景 { gcSprite spr = g_factory->CreateSpriteDefault(); spr->SetColor(createColor(0x0f0f0fff), 0); spr->SetColor(createColor(0x000000ff), 1); spr->SetColor(createColor(0x0f0f0fff), 2); spr->SetColor(createColor(0x2f2f2fff), 3); UI::Sprite* ui_spr = UI::Sprite::Create( spr, UI::Sprite::AutoSize::PANEL, UI::Sprite::FillMode::STRETCH); GetPanelRoot()->SetOrderMode(UI::Panel::FORCE); GetPanelRoot()->SetOrder(0.9f); GetPanelRoot()->Add(ui_spr); } // 难易度 时间 { UI::Text* txt = g_ui->CreateTextDefault(); txt->GetText()->SetColor(ColorDef::WHITE); txt->GetText()->SetFormat(true); txt->GetText()->SetLayoutAlign(Align::Direction::ZERO); txt->SetString("[c_green]请选择难度"); txt->SetElementData(Align::CT, { 727, 48 }); GetPanelRoot()->Add(txt); _uiTxt = txt; } // 返回按钮 { UI::ButtonSprite* btn = g_ui->CreateButtonSpriteDefault( UI::ButtonStyle::NORMAL); btn->SetString("退出"); btn->SetElementData(Align::LT, { 600, 500 }); GetPanelRoot()->Add(btn); _btnBack = btn; } // 难度菜单 { UI::Panel* panel = g_ui->CreatePanel(); panel->SetLocator(createLocatorY(32)); panel->SetPosPixel({ 600, 50 }); g_ui->SetPanelParent(panel, GetPanelRoot()); for(size_t i = 0;i < ARR_DIFF.size(); ++i) { UI::ButtonSprite* btn = g_ui->CreateButtonSpriteDefault( UI::ButtonStyle::NORMAL); btn->SetString(ARR_DIFF[i]); if (i != 0) btn->SetDisable(true); panel->Add(btn); } _panelMenu = panel; } // 数字菜单 { UI::Panel* panel = g_ui->CreatePanel(); panel->SetLocator(createLocatorX(32, 5)); panel->SetPosPixel({ 600, 50 + 200 }); g_ui->SetPanelParent(panel, GetPanelRoot()); for (size_t i = 0; i <= 9; ++i) { UI::ButtonSprite* btn = g_ui->CreateButtonSpriteDefault( UI::ButtonStyle::BOX); btn->SetMode(UI::Button::Mode::RADIO); btn->SetString(std::to_string(i)); panel->Add(btn); } _panelNumber = panel; } // 文字 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; node._number = 0; node._txt = g_factory->CreateText(0, 40, ColorDef::BLACK); node._txt->GetCoor()->SetParent(_coor); node._txt->SetOrder(ORDER_NUMBER); node._txt->SetPositionAlign(toPosition(60 * i + 20, 60 * j + 8), Align::CC); } } // 难度评分,种子值 { UI::Panel* panel = g_ui->CreatePanel(); panel->SetLocator(createLocatorY(32)); panel->SetPosPixel({ 600, 50 + 320 }); g_ui->SetPanelParent(panel, GetPanelRoot()); _panelDiff = panel; UI::Text* txt = g_ui->CreateTextDefault(); txt->GetText()->SetColor(ColorDef::WHITE); txt->GetText()->SetFormat(true); txt->GetText()->SetLayoutAlign(Align::Direction::ZERO); txt->SetString("[c_green]难度评分"); panel->Add(txt); UI::EditBox* edt = g_ui->CreateEditBoxDefault(); edt->GetPanel()->ExGetButtonSprite()->SetSpriteQuad( UI::Button::State::MAX_NUM, createQuad(Size{ 80, 16 }, false)); panel->Add(edt); UI::ButtonSprite* btn = g_ui->CreateButtonSpriteDefault( UI::ButtonStyle::NORMAL); btn->SetString("生成"); panel->Add(btn); } // 种子生成 { UI::Panel* panel = _panelDiff->Clone(); panel->SetPosPixel({ 600 + 90, 50 + 320 }); //panel->ExGetButtonSprite()->SetString("生成1"); _panelSeed = panel; } } void GameSudo::Update() { // 新游戏 std::size_t i = _panelMenu->GetClickIndex(); if (i != -1) { _diff = (int)i; if (_diff == 0) CreateSudo(32); else if (_diff == 1) CreateSudo(30); else if (_diff == 2) CreateSudo(27); else if (_diff == 3) CreateSudo(24); _tStart = g_time->GetTimeStamp(); _gameState = 1; } // 数字操作 { int number_old = _curNumber; if (g_input->KeyDown(KeyCode::KEY1)) _curNumber = 1; else if (g_input->KeyDown(KeyCode::KEY2)) _curNumber = 2; else if (g_input->KeyDown(KeyCode::KEY3)) _curNumber = 3; else if (g_input->KeyDown(KeyCode::KEY4)) _curNumber = 4; else if (g_input->KeyDown(KeyCode::KEY5)) _curNumber = 5; else if (g_input->KeyDown(KeyCode::KEY6)) _curNumber = 6; else if (g_input->KeyDown(KeyCode::KEY7)) _curNumber = 7; else if (g_input->KeyDown(KeyCode::KEY8)) _curNumber = 8; else if (g_input->KeyDown(KeyCode::KEY9)) _curNumber = 9; else if (g_input->KeyDown(KeyCode::KEY0) || g_input->KeyDown(KeyCode::GRAVE_ACCENT)) _curNumber = 0; if (number_old != _curNumber) { _panelNumber->SetSelect(_curNumber); } else { size_t i = _panelNumber->GetClickIndex(); if (i == -1) ; else _curNumber = (int)i; } } // 填入操作 if (g_input->KeyDown(MouseCode::LEFT)) { Position pos = _coor->WorldToThis(toPosition(g_input->GetCursorPosition())); Point xy = Pos2Xy(pos); int x = xy[0]; int y = xy[1]; if (CheckVaild(x, y, _curNumber) && !_vecNum[y][x]._bFixed) { _vecNum[y][x]._number = _curNumber; RefreshUI(); // 胜利检测 bool win = true; for (auto& iter : _vecNum) { if (iter._number == 0) { win = false; break; } } if (win) { _gameState = 2; gcText txt = g_factory->CreateText(0, 64); txt->SetString("恭喜,你过关!"); txt->SetPositionAlign(toPosition(g_system->GetWindowSize()) / 2.0f, Align::CC); ActionColor* act = new ActionColor(60, [=](ActionColor* act) mutable { txt->SetColor(act->GetColor()); txt->Render(); }); act->SetColor({ ColorDef::RED, ColorDef::GREEN }); g_action->Add(act); } } else { if (_gameState == 1 && _curNumber && IsInRange(x, y)) { std::string tips; if (_countErr < 10) tips = Random::Select(TIP_LV0); else if (_countErr < 20) tips = Random::Select(TIP_LV1); else tips = Random::Select(TIP_LV2); ++_countErr; gcText txt = g_factory->CreateText(0, 42); txt->SetString(tips); txt->SetPositionAlign(toPosition(g_input->GetCursorPosition()), Align::CC); ActionColor* act = new ActionColor(3, [=](ActionColor* act) mutable { txt->SetColor(act->GetColor()); txt->Render(); }); act->SetColor({ ColorDef::RED, createColor(0x00ff0000) }); g_action->Add(act); } } } if(_gameState == 1) _update_time(); if (_btnBack->IsClick()) { g_scene->Del("game"); g_scene->Add("main", new UIMain); } } void GameSudo::Render() { _coor->SetScale(g_ui->GetScale()); _coor->SetPosition(POS_LT); g_gfx->SetCoor(_coor); // 棋盘背景 g_gfx->SetColor(createColor(0xeeeeeeff)); g_gfx->SetOrder(ORDER_BOARD); g_gfx->RectangleSolid2D({}, SIZE_BOARD); // 网格线条(大) g_gfx->SetColor(createColor(0x0000aaff)); g_gfx->SetOrder(ORDER_GRID_LINE_0); for (size_t i = 0; i < 4; ++i) { float y = LENGTH_GRID * 3 * i; g_gfx->StrokeLine({ 0 , y }, { LENGTH_GRID * 9, y }, WIDTH_GRID_LINE_0); } for (size_t i = 0; i < 4; ++i) { float x = LENGTH_GRID * 3 * i; g_gfx->StrokeLine({ x , 0 }, { x, LENGTH_GRID * 9 }, WIDTH_GRID_LINE_0); } // 网格线条(小) g_gfx->SetColor(createColor(0x000077ff)); g_gfx->SetOrder(ORDER_GRID_LINE_1); for (int i = 0; i < 10; ++i) { float y = LENGTH_GRID * i; g_gfx->StrokeLine({0 , y }, { LENGTH_GRID * 9, y }, WIDTH_GRID_LINE_1); } for (int i = 0; i < 10; ++i) { float x = LENGTH_GRID * i; g_gfx->StrokeLine({ x, 0 }, { x, LENGTH_GRID * 9 }, WIDTH_GRID_LINE_1); } // 数字绘制 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; node._txt->Render(); if (_curNumber && node._number == _curNumber) { g_gfx->SetColor(toVector(ColorDef::RED, 20)); g_gfx->PolygonRegular2D(toPosition(60 * i + 30, 60 * j + 30), 25, 16); g_gfx->PolygonRegular2D(toPosition(60 * i + 30, 60 * j + 30), 26, 16); } } } // 光标绘制 Position pos = _coor->WorldToThis(toPosition(g_input->GetCursorPosition())); Point xy = Pos2Xy(pos); int x = xy[0]; int y = xy[1]; if (_curNumber && IsInRange(x, y)) { g_gfx->SetOrder(ORDER_MOUSE); g_gfx->SetColor(toVector(ColorDef::WHITE, 20)); g_gfx->PolygonRegularSolid2D(pos, 18, 16); g_gfx->SetColor(toVector(ColorDef::BLUE, 20)); g_gfx->PolygonRegular2D(pos, 18, 16); // 鼠标悬停 _txtMouse = g_factory->CreateText(0, 32, ColorDef::BLUE); _txtMouse->SetOrder(ORDER_MOUSE_TEXT); _txtMouse->GetCoor()->SetParent(_coor); _txtMouse->SetString(std::to_string(_curNumber)); _txtMouse->SetPositionAlign(pos, Align::CC); _txtMouse->Render(); g_system->SetCursorMode(System::CursorMode::HIDE); } else g_system->SetCursorMode(System::CursorMode::NORMAL); // UI::Button* btn = UI::Button::GetClickControl(); // if (btn) // { // gcText txt = g_factory->RenderText(); // txt->GetCoor()->SetParent(btn->GetCoor()); // txt->SetString("___________"); // } } void GameSudo::CreateSudo(int fixed_num) { rebuild: size_t remain_remove_num = 81 - fixed_num; Random::SetSeed(0); // 清空 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; node._number = 0; node._bFixed = true; } } // 铺满 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; size_t count = 0; while (true) { int n = Random::Int(1, 9); if (CheckVaild((int)i, (int)j, n)) { node._number = n; break; } ++count; if (count > 100) goto rebuild; } } } // 减少 size_t count = 0; while (remain_remove_num) { int i = Random::Int(0, 8); int j = Random::Int(0, 8); NumberNode& node = _vecNum[j][i]; if (node._number != 0) { node._number = 0; node._bFixed = false; --remain_remove_num; continue; } ++count; if (count > 100) goto rebuild; } // 验证解 size_t x = CheckSole(); if (x == 0) goto rebuild; // 去除答案 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; if (!node._bFixed) node._number = 0; } } { auto edt = _panelDiff->ExGetEditBox(0); if (edt) edt->SetString(std::to_string(x)); } { auto edt = _panelSeed->ExGetEditBox(0); if (edt) edt->SetString(std::to_string(x)); } RefreshUI(); } bool GameSudo::IsInRange(int x, int y) { if (x < 0 || x >= 9 || y < 0 || y >= 9) return false; return true; } size_t GameSudo::CheckSole() { size_t count = 0; do { // 统计剩余值 size_t remain = GetRemain(); if (remain == 0) return count; // 遍历空格子 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; if (node._number != 0) continue; int val = GetMustNumber((int)i, (int)j); if (val) node._number = val; ++count; } } // size_t remain_new = GetRemain(); if (remain_new == remain) return 0; } while (true); } size_t GameSudo::GetRemain() { size_t ret = 0; for (auto& iter : _vecNum) { if (iter._number == 0) ++ret; } return ret; } int GameSudo::GetMustNumber(int x, int y) { Array2, 9, 9> arr_can; // 计算当前局面,每一个空格子,可以填的数字(1-N个) // 遍历空格子 for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; if (node._number != 0) continue; for (int val = 1; val <= 9; ++val) { if (CheckVaild((int)i, (int)j, val)) { arr_can[j][i].push_back(val); } } } } std::list& vec = arr_can[y][x]; if (vec.size() == 1) return *vec.begin(); else return 0; } Point GameSudo::Pos2Xy(const Position& pos) { return toPoint(pos / LENGTH_GRID); } bool GameSudo::CheckVaild(int x, int y, int val) { if (val < 1 || val > 9) return false; if (!IsInRange(x, y)) return false; // 行检查 for (size_t i = 0; i < 9; ++i) { if (_vecNum[y][i]._number == val) return false; } // 列检查 for (size_t j = 0; j < 9; ++j) { if (_vecNum[j][x]._number == val) return false; } // 九宫检查 size_t start_y = y / 3 * 3; size_t start_x = x / 3 * 3; for (size_t j = 0; j < 3; ++j) { for (size_t i = 0; i < 3; ++i) { if (_vecNum[j + start_y][i + start_x]._number == val) return false; } } return true; } void GameSudo::RefreshUI() { for (size_t j = 0; j < 9; ++j) { for (size_t i = 0; i < 9; ++i) { NumberNode& node = _vecNum[j][i]; if (node._bFixed) node._txt->SetColor(ColorDef::BLACK); else node._txt->SetColor(createColor(0x00aa00ff)); if (node._number > 0) node._txt->SetString(std::to_string(node._number)); else node._txt->SetString({}); } } } void GameSudo::_update_time() { std::string str_time = "00:00:00"; if(_tStart) str_time = g_time->TimeStampDelta2String(g_time->GetTimeStamp() - _tStart); std::string str = fmt::format( "[s_32]难易度:\n[c_green]{}\n[c]时间:\n[c_yellow]{}", ARR_DIFF[_diff], str_time ); _uiTxt->SetString(str); }