Files
dl/project/dl_test/game_sudo.cpp
T
2026-09-16 14:07:40 +08:00

635 lines
14 KiB
C++

#include "game_sudo.h"
#include "ui_main.h"
constexpr std::array<std::string_view, 4> ARR_DIFF = {
"简单", "中间", "高级", "高级+"
};
constexpr std::array<std::string_view, 4> TIP_LV0 = {
"此处不能填入当前数字。",
"请确保横、竖、九宫都没有重复数字。"
};
constexpr std::array<std::string_view, 4> TIP_LV1 = {
"眼花了吧,这里不能放!",
"请看仔细一点!"
"不要点歪了!"
};
constexpr std::array<std::string_view, 4> 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<std::list<int>, 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<int>& 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);
}