/*
* Copyright 2016-2018, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* panaconda.cpp -- example usage of libpmemobj C++ bindings
*/
#ifdef __FreeBSD__
#define _WITH_GETLINE
#endif
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <getopt.h>
#include <iostream>
#ifdef __FreeBSD__
#include <ncurses/ncurses.h> /* Need pkg, not system, version */
#else
#include <ncurses.h>
#endif
#include "panaconda.hpp"
#define LAYOUT_NAME "panaconda"
#define DEFAULT_DELAY 120000
#define SNAKE_START_POS_X 5
#define SNAKE_START_POS_Y 5
#define SNAKE_START_DIR (direction::RIGHT)
#define SNAKE_STAR_SEG_NO 5
#define BOARD_STATIC_SIZE_ROW 40
#define BOARD_STATIC_SIZE_COL 30
#define PLAYER_POINTS_PER_HIT 10
using examples::list;
using pmem::pool_error;
using pmem::transaction_error;
using pmem::transaction_scope_error;
using pmem::obj::delete_persistent;
using pmem::obj::make_persistent;
using pmem::obj::persistent_ptr;
using pmem::obj::pool;
using pmem::obj::transaction;
/*
* Color_pair
*/
color_pair::color_pair() : color_bg(COLOR_BLACK), color_fg(COLOR_BLACK)
{
}
color_pair::color_pair(const int col_fg, const int col_bg)
: color_bg(col_bg), color_fg(col_fg)
{
}
/*
* Helper
*/
struct color_pair
helper::get_color(const object_type obj_type)
{
struct color_pair res;
switch (obj_type) {
case SNAKE_SEGMENT:
res = color_pair(COLOR_WHITE, COLOR_BLACK);
break;
case WALL:
res = color_pair(COLOR_BLUE, COLOR_BLUE);
break;
case FOOD:
res = color_pair(COLOR_RED, COLOR_BLACK);
break;
default:
std::cout << "Error: get_color - wrong value passed!"
<< std::endl;
assert(0);
}
return res;
}
int
helper::parse_params(int argc, char *argv[], struct parameters *par)
{
int opt;
std::string app = argv[0];
while ((opt = getopt(argc, argv, "m:")) != -1) {
switch (opt) {
case 'm':
par->use_maze = true;
par->maze_path = optarg;
break;
default:
helper::print_usage(app);
return -1;
}
}
if (optind < argc) {
par->name = argv[optind];
} else {
helper::print_usage(app);
return -1;
}
return 0;
}
inline void
helper::sleep(int time)
{
clock_t curr_time = clock();
while (clock() < (curr_time + time)) {
}
}
inline void
helper::print_usage(std::string &name)
{
std::cout << "Usage: " << name << " [-m <maze_path>] <pool_name>\n";
}
/*
* Point
*/
point::point() : x(0), y(0)
{
}
point::point(int x, int y) : x(x), y(y)
{
}
bool
operator==(point &point1, point &point2)
{
return point1.x == point2.x && point1.y == point2.y;
}
/*
* Shape
*/
element_shape::element_shape(int shape)
{
int n_curves_symbol = get_symbol(shape);
val = COLOR_PAIR(shape) | n_curves_symbol;
}
int
element_shape::get_val()
{
return val;
}
int
element_shape::get_symbol(int shape)
{
int symbol = 0;
switch (shape) {
case SNAKE_SEGMENT:
symbol = ACS_DIAMOND;
break;
case WALL:
symbol = ACS_BLOCK;
break;
case FOOD:
symbol = ACS_CKBOARD;
break;
default:
symbol = ACS_DIAMOND;
break;
}
return symbol;
}
/*
* Element
*/
board_element::board_element()
: position(make_persistent<point>(0, 0)),
shape(make_persistent<element_shape>(SNAKE_SEGMENT)),
element_dir(direction::LEFT)
{
}
board_element::board_element(int px, int py,
pmem::obj::persistent_ptr<element_shape> shape,
direction dir)
: position(make_persistent<point>(px, py)), shape(shape), element_dir(dir)
{
}
board_element::board_element(point p,
pmem::obj::persistent_ptr<element_shape> shape,
direction dir)
: position(make_persistent<point>(p.x, p.y)), shape(shape), element_dir(dir)
{
}
board_element::board_element(const board_element &element)
: position(
make_persistent<point>(element.position->x, element.position->y)),
shape(element.shape),
element_dir(UNDEFINED)
{
}
board_element::~board_element()
{
pmem::obj::delete_persistent<point>(position);
position = nullptr;
pmem::obj::delete_persistent<element_shape>(shape);
shape = nullptr;
}
persistent_ptr<point>
board_element::calc_new_position(const direction dir)
{
persistent_ptr<point> pt =
make_persistent<point>(position->x, position->y);
switch (dir) {
case direction::DOWN:
pt->y = pt->y + 1;
break;
case direction::LEFT:
pt->x = pt->x - 1;
break;
case direction::RIGHT:
pt->x = pt->x + 1;
break;
case direction::UP:
pt->y = pt->y - 1;
break;
default:
break;
}
return pt;
}
void
board_element::set_position(const persistent_ptr<point> new_point)
{
position = new_point;
}
persistent_ptr<point>
board_element::get_position(void)
{
return position;
}
void
board_element::print(void)
{
mvaddch(position->y, position->x, shape->get_val());
}
void
board_element::print_double_col(void)
{
mvaddch(position->y, (2 * position->x), shape->get_val());
}
void
board_element::print_single_double_col(void)
{
mvaddch(position->y, (2 * position->x), shape->get_val());
mvaddch(position->y, (2 * position->x - 1), shape->get_val());
}
direction
board_element::get_direction(void)
{
return element_dir;
}
void
board_element::set_direction(const direction dir)
{
element_dir = dir;
}
/*
* Snake
*/
snake::snake()
{
snake_segments = make_persistent<list<board_element>>();
for (unsigned i = 0; i < SNAKE_STAR_SEG_NO; ++i) {
persistent_ptr<element_shape> shape =
make_persistent<element_shape>(SNAKE_SEGMENT);
persistent_ptr<board_element> element =
make_persistent<board_element>(SNAKE_START_POS_X - i,
SNAKE_START_POS_Y, shape,
SNAKE_START_DIR);
snake_segments->push_back(element);
}
last_seg_position = point();
last_seg_dir = direction::RIGHT;
}
snake::~snake()
{
snake_segments->clear();
delete_persistent<list<board_element>>(snake_segments);
}
void
snake::move(const direction dir)
{
int snake_size = snake_segments->size();
persistent_ptr<point> new_position_point;
last_seg_position =
*(snake_segments->get(snake_size - 1)->get_position().get());
last_seg_dir = snake_segments->get(snake_size - 1)->get_direction();
for (int i = (snake_size - 1); i >= 0; --i) {
if (i == 0) {
new_position_point =
snake_segments->get(i)->calc_new_position(dir);
snake_segments->get(i)->set_direction(dir);
} else {
new_position_point =
snake_segments->get(i)->calc_new_position(
snake_segments->get(i - 1)
->get_direction());
snake_segments->get(i)->set_direction(
snake_segments->get(i - 1)->get_direction());
}
snake_segments->get(i)->set_position(new_position_point);
}
}
void
snake::print(void)
{
int i = 0;
persistent_ptr<board_element> segp;
while ((segp = snake_segments->get(i++)) != nullptr)
segp->print_double_col();
}
void
snake::add_segment(void)
{
persistent_ptr<element_shape> shape =
make_persistent<element_shape>(SNAKE_SEGMENT);
persistent_ptr<board_element> segp = make_persistent<board_element>(
last_seg_position, shape, last_seg_dir);
snake_segments->push_back(segp);
}
bool
snake::check_point_against_segments(point point)
{
int i = 0;
bool result = false;
persistent_ptr<board_element> segp;
while ((segp = snake_segments->get(i++)) != nullptr) {
if (point == *(segp->get_position().get())) {
result = true;
break;
}
}
return result;
}
point
snake::get_head_point(void)
{
return *(snake_segments->get(0)->get_position().get());
}
direction
snake::get_direction(void)
{
return snake_segments->get(0)->get_direction();
}
point
snake::get_next_point(const direction dir)
{
return *(snake_segments->get(0)->calc_new_position(dir).get());
}
/*
* Board
*/
game_board::game_board()
{
persistent_ptr<element_shape> shape =
make_persistent<element_shape>(FOOD);
food = make_persistent<board_element>(0, 0, shape,
direction::UNDEFINED);
layout = make_persistent<list<board_element>>();
anaconda = make_persistent<snake>();
size_row = 20;
size_col = 20;
}
game_board::~game_board()
{
layout->clear();
delete_persistent<list<board_element>>(layout);
delete_persistent<snake>(anaconda);
delete_persistent<board_element>(food);
}
void
game_board::print(const int score)
{
const int offset_y = 2 * size_col + 5;
const int offset_x = 2;
int i = 0;
persistent_ptr<board_element> elmp;
while ((elmp = layout->get(i++)) != nullptr)
elmp->print_single_double_col();
anaconda->print();
food->print_double_col();
mvprintw((offset_x + 0), offset_y, " ##### panaconda ##### ");
mvprintw((offset_x + 1), offset_y, " # # ");
mvprintw((offset_x + 2), offset_y, " # q - quit # ");
mvprintw((offset_x + 3), offset_y, " # n - new game # ");
mvprintw((offset_x + 4), offset_y, " # # ");
mvprintw((offset_x + 5), offset_y, " ##################### ");
mvprintw((offset_x + 7), offset_y, " Score: %d ", score);
}
void
game_board::print_game_over(const int score)
{
int x = size_col / 3;
int y = size_row / 6;
mvprintw(y + 0, x, "####### ####### # # #######");
mvprintw(y + 1, x, "# # # ## ## # ");
mvprintw(y + 2, x, "# ### ####### # # # # #### ");
mvprintw(y + 3, x, "# # # # # # # # ");
mvprintw(y + 4, x, "####### # # # # #######");
mvprintw(y + 6, x, "####### # # ####### #######");
mvprintw(y + 7, x, "# # # # # # #");
mvprintw(y + 8, x, "# # # # #### #######");
mvprintw(y + 9, x, "# # # # # # # ");
mvprintw(y + 10, x, "####### # ####### # #");
mvprintw(y + 12, x, " Last score: %d ", score);
mvprintw(y + 14, x, " q - quit");
mvprintw(y + 15, x, " n - new game");
}
int
game_board::creat_dynamic_layout(const unsigned row_no, char *const buffer)
{
persistent_ptr<board_element> element;
persistent_ptr<element_shape> shape;
for (unsigned i = 0; i < size_col; ++i) {
if (buffer[i] == config_file_symbol::SYM_WALL) {
shape = make_persistent<element_shape>(WALL);
element = make_persistent<board_element>(
i, row_no, shape, direction::UNDEFINED);
layout->push_back(element);
}
}
return 0;
}
int
game_board::creat_static_layout(void)
{
persistent_ptr<board_element> element;
persistent_ptr<element_shape> shape;
size_row = BOARD_STATIC_SIZE_ROW;
size_col = BOARD_STATIC_SIZE_COL;
// first and last row
for (unsigned i = 0; i < size_col; ++i) {
shape = make_persistent<element_shape>(WALL);
element = make_persistent<board_element>(i, 0, shape,
direction::UNDEFINED);
layout->push_back(element);
shape = make_persistent<element_shape>(WALL);
element = make_persistent<board_element>(
i, (size_row - 1), shape, direction::UNDEFINED);
layout->push_back(element);
}
// middle rows
for (unsigned i = 1; i < size_row; ++i) {
shape = make_persistent<element_shape>(WALL);
element = make_persistent<board_element>(0, i, shape,
direction::UNDEFINED);
layout->push_back(element);
shape = make_persistent<element_shape>(WALL);
element = make_persistent<board_element>(
(size_col - 1), i, shape, direction::UNDEFINED);
layout->push_back(element);
}
return 0;
}
bool
game_board::is_snake_collision(point point)
{
return anaconda->check_point_against_segments(point);
}
bool
game_board::is_wall_collision(point point)
{
int i = 0;
bool result = false;
persistent_ptr<board_element> wallp;
while ((wallp = layout->get(i++)) != nullptr) {
if (point == *(wallp->get_position().get())) {
result = true;
break;
}
}
return result;
}
bool
game_board::is_collision(point point)
{
return is_snake_collision(point) || is_wall_collision(point);
}
bool
game_board::is_snake_head_food_hit(void)
{
bool result = false;
point head_point = anaconda->get_head_point();
if (head_point == *(food->get_position().get())) {
result = true;
}
return result;
}
void
game_board::set_new_food(const point point)
{
persistent_ptr<element_shape> shape =
make_persistent<element_shape>(FOOD);
delete_persistent<board_element>(food);
food = make_persistent<board_element>(point, shape,
direction::UNDEFINED);
}
void
game_board::create_new_food(void)
{
const int max_repeat = 50;
int count = 0;
int rand_row = 0;
int rand_col = 0;
while (count < max_repeat) {
rand_row = 1 + rand() % (get_size_row() - 2);
rand_col = 1 + rand() % (get_size_col() - 2);
point food_point(rand_col, rand_row);
if (!is_collision(food_point)) {
set_new_food(food_point);
break;
}
count++;
}
}
snake_event
game_board::move_snake(const direction dir)
{
snake_event event = snake_event::EV_OK;
point next_pt = anaconda->get_next_point(dir);
if (is_collision(next_pt)) {
event = snake_event::EV_COLLISION;
} else {
anaconda->move(dir);
}
return event;
}
void
game_board::add_snake_segment(void)
{
anaconda->add_segment();
}
unsigned
game_board::get_size_row(void)
{
return size_row;
}
void
game_board::set_size_row(const unsigned size_r)
{
size_row = size_r;
}
unsigned
game_board::get_size_col(void)
{
return size_col;
}
void
game_board::set_size_col(const unsigned size_c)
{
size_col = size_c;
}
direction
game_board::get_snake_dir(void)
{
return anaconda->get_direction();
}
/*
* Game_state
*/
game_state::game_state()
{
}
game_state::~game_state()
{
}
pmem::obj::persistent_ptr<game_board>
game_state::get_board()
{
return board;
}
pmem::obj::persistent_ptr<game_player>
game_state::get_player()
{
return player;
}
void
game_state::init(void)
{
board = make_persistent<game_board>();
player = make_persistent<game_player>();
}
void
game_state::clean_pool(void)
{
delete_persistent<game_board>(board);
board = nullptr;
delete_persistent<game_player>(player);
player = nullptr;
}
/*
* Player
*/
game_player::game_player() : score(0), state(STATE_PLAY)
{
}
game_player::~game_player()
{
}
int
game_player::get_score(void)
{
return score;
}
play_state
game_player::get_state(void)
{
return state;
}
void
game_player::set_state(const play_state st)
{
state = st;
}
void
game_player::update_score(void)
{
score = score + PLAYER_POINTS_PER_HIT;
}
/*
* Game
*/
game::game(struct parameters *par)
{
pool<game_state> pop;
initscr();
start_color();
nodelay(stdscr, true);
curs_set(0);
keypad(stdscr, true);
params = par;
if (pool<game_state>::check(params->name, LAYOUT_NAME) == 1)
pop = pool<game_state>::open(params->name, LAYOUT_NAME);
else
pop = pool<game_state>::create(params->name, LAYOUT_NAME,
PMEMOBJ_MIN_POOL * 10, 0666);
state = pop;
direction_key = direction::UNDEFINED;
last_key = KEY_CLEAR;
delay = DEFAULT_DELAY;
init_colors();
srand(time(nullptr));
}
game::~game()
{
}
void
game::init_colors(void)
{
struct color_pair color_pair = helper::get_color(SNAKE_SEGMENT);
init_pair(SNAKE_SEGMENT, color_pair.color_fg, color_pair.color_bg);
color_pair = helper::get_color(WALL);
init_pair(WALL, color_pair.color_fg, color_pair.color_bg);
color_pair = helper::get_color(FOOD);
init_pair(FOOD, color_pair.color_fg, color_pair.color_bg);
}
void
game::init(void)
{
int ret = 0;
persistent_ptr<game_state> r = state.root();
if (r->get_board() == nullptr) {
transaction::run(state, [&r, &ret, this]() {
r->init();
if (params->use_maze)
ret = parse_conf_create_dynamic_layout();
else
ret = r->get_board()->creat_static_layout();
r->get_board()->create_new_food();
});
if (ret) {
clean_pool();
clear_prog();
throw std::runtime_error("Error: Config file error!");
}
}
direction_key = r->get_board()->get_snake_dir();
}
void
game::process_step(void)
{
snake_event ret_event = EV_OK;
persistent_ptr<game_state> r = state.root();
transaction::run(state, [&]() {
ret_event = r->get_board()->move_snake(direction_key);
if (EV_COLLISION == ret_event) {
r->get_player()->set_state(play_state::STATE_GAMEOVER);
return;
} else {
if (r->get_board()->is_snake_head_food_hit()) {
r->get_board()->create_new_food();
r->get_board()->add_snake_segment();
r->get_player()->update_score();
}
}
});
r->get_board()->print(r->get_player()->get_score());
}
inline bool
game::is_stopped(void)
{
return action::ACTION_QUIT == last_key;
}
void
game::set_direction_key(void)
{
switch (last_key) {
case KEY_LEFT:
if (direction::RIGHT != direction_key)
direction_key = direction::LEFT;
break;
case KEY_RIGHT:
if (direction::LEFT != direction_key)
direction_key = direction::RIGHT;
break;
case KEY_UP:
if (direction::DOWN != direction_key)
direction_key = direction::UP;
break;
case KEY_DOWN:
if (direction::UP != direction_key)
direction_key = direction::DOWN;
break;
default:
break;
}
}
void
game::process_key(const int lastkey)
{
last_key = lastkey;
set_direction_key();
if (action::ACTION_NEW_GAME == last_key) {
clean_pool();
init();
}
}
void
game::clean_pool(void)
{
persistent_ptr<game_state> r = state.root();
transaction::run(state, [&]() { r->clean_pool(); });
}
void
game::process_delay(void)
{
helper::sleep(delay);
}
void
game::clear_screen(void)
{
erase();
}
void
game::game_over(void)
{
persistent_ptr<game_state> r = state.root();
r->get_board()->print_game_over(r->get_player()->get_score());
}
bool
game::is_game_over(void)
{
persistent_ptr<game_state> r = state.root();
return (r->get_player()->get_state() == play_state::STATE_GAMEOVER);
}
void
game::clear_prog(void)
{
state.close();
endwin();
}
int
game::parse_conf_create_dynamic_layout(void)
{
FILE *cfg_file;
char *line = nullptr;
size_t len = 0;
unsigned i = 0;
ssize_t col_no = 0;
cfg_file = fopen(params->maze_path.c_str(), "r");
if (cfg_file == nullptr)
return -1;
persistent_ptr<game_state> r = state.root();
while ((col_no = getline(&line, &len, cfg_file)) != -1) {
if (i == 0)
r->get_board()->set_size_col(col_no - 1);
try {
transaction::run(state, [&]() {
r->get_board()->creat_dynamic_layout(i, line);
});
} catch (transaction_error &err) {
std::cout << err.what() << std::endl;
} catch (transaction_scope_error &tse) {
std::cout << tse.what() << std::endl;
}
i++;
}
r->get_board()->set_size_row(i);
free(line);
fclose(cfg_file);
return 0;
}
/*
* main
*/
int
main(int argc, char *argv[])
{
struct parameters params;
params.use_maze = false;
if (helper::parse_params(argc, argv, ¶ms))
return -1;
int ret = -1;
try {
std::unique_ptr<game> snake_game{new game(¶ms)};
snake_game->init();
while (!snake_game->is_stopped()) {
int input = getch();
snake_game->process_key(input);
if (snake_game->is_game_over()) {
snake_game->game_over();
} else {
snake_game->process_delay();
snake_game->clear_screen();
snake_game->process_step();
}
}
snake_game->clear_prog();
ret = 0;
} catch (transaction_error &err) {
std::cout << err.what() << std::endl;
} catch (transaction_scope_error &tse) {
std::cout << tse.what() << std::endl;
} catch (pool_error &pe) {
std::cout << pe.what() << std::endl;
} catch (std::logic_error &le) {
std::cout << le.what() << std::endl;
} catch (std::runtime_error &re) {
std::cout << re.what() << std::endl;
}
return ret;
}