mirror of
https://gitee.com/yanmu_ym/cpp.git
synced 2026-02-03 09:33:16 +08:00
370 lines
8.6 KiB
C++
370 lines
8.6 KiB
C++
#include <cmath>
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#include <memory.h>
|
||
#include <map>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
#include "common.h"
|
||
#include "scene.h"
|
||
#include "utility.inl"
|
||
|
||
CScene::CScene(int index)
|
||
: _max_column(pow(index, 2))
|
||
, _cur_point({0, 0})
|
||
{
|
||
init();
|
||
}
|
||
|
||
CScene::~CScene()
|
||
{
|
||
if(keyMap) delete keyMap;
|
||
}
|
||
|
||
void CScene::show() const
|
||
{
|
||
cls();
|
||
|
||
printUnderline();
|
||
|
||
for (int row = 0; row < _max_column; ++row)
|
||
{
|
||
CBlock block = _row_block[row];
|
||
block.print();
|
||
printUnderline(row);
|
||
}
|
||
}
|
||
|
||
void CScene::setMode(KeyMode mode)
|
||
{
|
||
switch (mode)
|
||
{
|
||
case KeyMode::NORMAL:
|
||
keyMap = new Normal;
|
||
break;
|
||
|
||
case KeyMode::VIM:
|
||
keyMap = new Vim;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void CScene::printUnderline(int line_no) const
|
||
{
|
||
for (int colunm = 0; colunm < 9; ++colunm)
|
||
std::cout << "\u254B" << "\u2501" << ((_cur_point.y == line_no && _cur_point.x == colunm)?"^":"\u2501") << "\u2501";
|
||
std::cout << "\u254B" << std::endl;
|
||
}
|
||
|
||
void CScene::init()
|
||
{
|
||
memset(_map, UNSELECTED, sizeof _map);
|
||
|
||
// column_block 所有列
|
||
for (int col = 0; col < _max_column; ++col)
|
||
{
|
||
CBlock column_block;
|
||
|
||
for (int row = 0; row < _max_column; ++row)
|
||
{
|
||
column_block.push_back(_map + row * 9 + col);
|
||
}
|
||
_column_block[col] = column_block;
|
||
}
|
||
|
||
// row_block 所有行
|
||
for (int row = 0; row < _max_column; ++row)
|
||
{
|
||
CBlock row_block;
|
||
|
||
for (int col = 0; col < _max_column; ++col)
|
||
{
|
||
row_block.push_back(_map + row * 9 + col);
|
||
}
|
||
_row_block[row] = row_block;
|
||
}
|
||
|
||
// xy_block 所有九宫格, [行][列]
|
||
for (int block_index = 0; block_index < _max_column; ++block_index)
|
||
{
|
||
CBlock xy_block;
|
||
|
||
int xy_begin = block_index / 3 * 27 + block_index % 3 * 3;
|
||
for (int i = 0; i < 3; i++) {
|
||
for (int j = 0; j < 3; j++) {
|
||
xy_block.push_back(_map + xy_begin + i * 9 + j);
|
||
}
|
||
}
|
||
_xy_block[block_index / 3][block_index % 3] = xy_block;
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
bool CScene::setCurValue(const int nCurValue, int &nLastValue)
|
||
{
|
||
auto point = _map[_cur_point.x + _cur_point.y * 9];
|
||
if (point.state == State::ERASED)
|
||
{
|
||
nLastValue = point.value;
|
||
setValue(nCurValue);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
void CScene::setValue(const point_t& p, const int value)
|
||
{
|
||
_map[p.x + p.y * 9].value = value;
|
||
}
|
||
|
||
void CScene::setValue(const int value)
|
||
{
|
||
auto p = _cur_point;
|
||
this->setValue(p, value);
|
||
}
|
||
|
||
// 选择count个格子清空
|
||
void CScene::eraseRandomGrids(const int count)
|
||
{
|
||
point_value_t p = {UNSELECTED, State::ERASED};
|
||
|
||
std::vector<int> v(81);
|
||
for (int i = 0; i < 81; ++i) {
|
||
v[i] = i;
|
||
}
|
||
|
||
for (int i = 0; i < count; ++i) {
|
||
int r = random(0, v.size() - 1);
|
||
_map[v[r]] = p;
|
||
v.erase(v.begin() + r);
|
||
}
|
||
}
|
||
|
||
bool CScene::isComplete()
|
||
{
|
||
// 任何一个block未被填满,则肯定未完成
|
||
for (size_t i = 0; i < 81; ++i)
|
||
{
|
||
if (_map[i].value == UNSELECTED)
|
||
return false;
|
||
}
|
||
|
||
// 同时block里的数字还要符合规则
|
||
for (int row = 0; row < 9; ++row)
|
||
{
|
||
for (int col = 0; col < 9; ++col)
|
||
{
|
||
if (!_row_block[row].isValid() ||
|
||
!_column_block[col].isValid() ||
|
||
!_xy_block[row / 3][col / 3].isValid())
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
void CScene::save(const char *filename) {
|
||
std::fstream fs;
|
||
// TODO: check whether the file has existed
|
||
fs.open(filename, std::fstream::in | std::fstream::out | std::fstream::app);
|
||
|
||
// save _map
|
||
for (int i = 0; i < 81; i++) {
|
||
fs << _map[i].value << ' ' << static_cast<int>(_map[i].state) << std::endl;
|
||
}
|
||
|
||
// save _cur_point
|
||
fs << _cur_point.x << ' ' << _cur_point.y << std::endl;
|
||
|
||
// save _vCommand
|
||
fs << _vCommand.size() << std::endl;
|
||
for (CCommand command : _vCommand) {
|
||
point_t point = command.getPoint();
|
||
fs << point.x << ' ' << point.y << ' '
|
||
<< command.getPreValue() << ' '
|
||
<< command.getCurValue() << std::endl;
|
||
}
|
||
|
||
fs.close();
|
||
}
|
||
|
||
void CScene::load(const char *filename) {
|
||
std::fstream fs;
|
||
// TODO: check whether the file has existed
|
||
fs.open(filename, std::fstream::in | std::fstream::out | std::fstream::app);
|
||
|
||
// load _map
|
||
for (int i = 0; i < 81; i++) {
|
||
int tmpState;
|
||
fs >> _map[i].value >> tmpState;
|
||
_map[i].state = static_cast<State>(tmpState);
|
||
}
|
||
|
||
// load _cur_point
|
||
fs >> _cur_point.x >> _cur_point.y;
|
||
|
||
// load _vCommand
|
||
int commandSize;
|
||
fs >> commandSize;
|
||
for (int i = 0; i < commandSize; i++) {
|
||
point_t point;
|
||
int preValue, curValue;
|
||
fs >> point.x >> point.y >> preValue >> curValue;
|
||
_vCommand.emplace_back(this, point, preValue, curValue);
|
||
}
|
||
}
|
||
|
||
void CScene::play()
|
||
{
|
||
show();
|
||
|
||
char key = '\0';
|
||
while (1)
|
||
{
|
||
key = getch();
|
||
if (key >= '0' && key <= '9')
|
||
{
|
||
CCommand oCommand(this);
|
||
if (!oCommand.execute(key - '0'))
|
||
{
|
||
std::cout << "this number can't be modified." << std::endl;
|
||
}
|
||
else
|
||
{
|
||
_vCommand.push_back(std::move(oCommand)); // XXX: move without move constructor
|
||
show();
|
||
continue;
|
||
}
|
||
}
|
||
if (key == keyMap->ESC)
|
||
{
|
||
message("quit game ? [Y/N]");
|
||
std::string strInput;
|
||
std::cin >> strInput;
|
||
if (strInput[0] == 'y' || strInput[0] == 'Y')
|
||
{
|
||
message("do you want to save the game progress ? [Y/N]");
|
||
std::cin >> strInput;
|
||
if (strInput[0] == 'y' || strInput[0] == 'Y')
|
||
{
|
||
message("input path of the progress file: ", false);
|
||
std::cin >> strInput;
|
||
save(strInput.c_str());
|
||
}
|
||
exit(0);
|
||
} else
|
||
{
|
||
message("continue.");
|
||
}
|
||
}
|
||
else if (key == keyMap->U)
|
||
{
|
||
if (_vCommand.empty())
|
||
message("no more action to undo.");
|
||
else
|
||
{
|
||
CCommand& oCommand = _vCommand.back();
|
||
oCommand.undo();
|
||
_vCommand.pop_back();
|
||
show();
|
||
}
|
||
}
|
||
else if (key == keyMap->LEFT)
|
||
{
|
||
_cur_point.x = (_cur_point.x - 1) < 0 ? 0 : _cur_point.x - 1;
|
||
show();
|
||
}
|
||
else if (key == keyMap->RIGHT)
|
||
{
|
||
_cur_point.x = (_cur_point.x + 1) > 8 ? 8 : _cur_point.x + 1;
|
||
show();
|
||
}
|
||
else if (key == keyMap->DOWN)
|
||
{
|
||
_cur_point.y = (_cur_point.y + 1) > 8 ? 8 : _cur_point.y + 1;
|
||
show();
|
||
}
|
||
else if (key == keyMap->UP)
|
||
{
|
||
_cur_point.y = (_cur_point.y - 1) < 0 ? 0 : _cur_point.y - 1;
|
||
show();
|
||
}
|
||
else if (key == keyMap->ENTER)
|
||
{
|
||
if (isComplete())
|
||
{
|
||
message("congratulation! you win!");
|
||
getchar();
|
||
exit(0);
|
||
}
|
||
else
|
||
{
|
||
message("sorry, not completed.");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 一个场景可以多次被初始化
|
||
void CScene::generate()
|
||
{
|
||
// XXX: pseudo random
|
||
static char map_pattern[10][10] = {
|
||
"ighcabfde",
|
||
"cabfdeigh",
|
||
"fdeighcab",
|
||
"ghiabcdef",
|
||
"abcdefghi",
|
||
"defghiabc",
|
||
"higbcaefd",
|
||
"bcaefdhig",
|
||
"efdhigbca"};
|
||
|
||
std::vector<char> v = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'};
|
||
|
||
// 产生字母到数字的随机映射
|
||
std::unordered_map<char, int> hash_map;
|
||
for (int i = 1; i <= 9; ++i)
|
||
{
|
||
int r = random(0, v.size() - 1);
|
||
hash_map[v[r]] = i;
|
||
v.erase(v.begin() + r);
|
||
}
|
||
|
||
// 填入场景
|
||
for (int row = 0; row < 9; ++row)
|
||
{
|
||
for (int col = 0; col < 9; ++col)
|
||
{
|
||
point_t point = {row, col};
|
||
char key = map_pattern[row][col];
|
||
setValue(point, hash_map[key]);
|
||
}
|
||
}
|
||
|
||
assert(isComplete());
|
||
|
||
return;
|
||
}
|
||
|
||
bool CScene::setPointValue(const point_t &stPoint, const int nValue)
|
||
{
|
||
auto point = _map[stPoint.x + stPoint.y * 9];
|
||
if (State::ERASED == point.state)
|
||
{
|
||
_cur_point = stPoint;
|
||
setValue(nValue);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
point_t CScene::getCurPoint()
|
||
{
|
||
return _cur_point;
|
||
}
|