635 lines
14 KiB
C++
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);
|
|
}
|
|
|