Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions fw/Core/Hitcon/App/MainMenuApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ using hitcon::app::tetris::tetris_app;

constexpr menu_entry_t main_menu_entries[] = {
// TODO : change app
{"Spaceship", &spaceship_app, nullptr},
{"Hacker Pet", &tama_app, &hitcon::app::tama::SetSingleplayer},
{"Snake", &snake_app, &hitcon::app::snake::SetSingleplayer},
{"Tetris", &tetris_app, &hitcon::app::tetris::SetSingleplayer},
Expand Down
105 changes: 50 additions & 55 deletions fw/Core/Hitcon/App/SpaceshipApp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
#include <Service/Sched/Scheduler.h>
#include <Util/uint_to_str.h>

#include <algorithm>

using namespace hitcon::service::sched;
using hitcon::game::EventType;

Expand All @@ -29,7 +27,7 @@ void SpaceshipApp::Init() { scheduler.Queue(&_routine_task, nullptr); }
// 1. OnEntry: menu -> ready
void SpaceshipApp::OnEntry() {
_state = INIT;
display_set_mode_scroll_text("Ready...");
> display_set_mode_scroll_text("Ready...");
}
// 2. OnExit
void SpaceshipApp::OnExit() {
Expand All @@ -42,8 +40,6 @@ void SpaceshipApp::OnExit() {
void SpaceshipApp::OnButton(button_t button) {}
// 4. OnEdgeButton
void SpaceshipApp::OnEdgeButton(button_t button) {
constexpr int last_row = DISPLAY_WIDTH * (DISPLAY_HEIGHT - 2);

if ((button & BUTTON_VALUE_MASK) == BUTTON_BACK) {
badge_controller.BackToMenu(this);
return;
Expand All @@ -52,15 +48,17 @@ void SpaceshipApp::OnEdgeButton(button_t button) {
if (_state == RUN) {
switch (button & BUTTON_VALUE_MASK) {
case BUTTON_DOWN: // move _my_position down
if (_my_position < last_row) _SyncMyPlane(_my_position + DISPLAY_WIDTH);
if (_my_position < PLANE_UPPER_BOUND) {
_SyncMyPlane(_my_position << 1);
}
break;
case BUTTON_UP: // move _my_position up
if (_my_position > DISPLAY_WIDTH)
_SyncMyPlane(_my_position - DISPLAY_WIDTH);
if (_my_position > PLANE_LOWER_BOUND) {
_SyncMyPlane(_my_position >> 1);
}
break;
case BUTTON_RIGHT: // emit bullet
_bullets[_num_bullets] = _my_position + 2;
_num_bullets += 1;
_bullets[2] |= _my_position;
break;
default:
break;
Expand All @@ -76,14 +74,18 @@ void SpaceshipApp::OnEdgeButton(button_t button) {

void SpaceshipApp::_StartGame() {
// setup my plane
_SyncMyPlane(DISPLAY_WIDTH);
_my_plane[2] = {0};
_my_position = 0;
_SyncMyPlane(PLANE_LOWER_BOUND);
// setup enemy
_enemy_position[DISPLAY_WIDTH] = {0};
_GenerateEnemy();
_has_enemy = true;
// setup bullets
_num_bullets = 0;
// setup score
_score = 0;
// setup bullets
_bullets[DISPLAY_WIDTH] = {0};
// setup display
_frame_buf[DISPLAY_WIDTH] = {0};
// setup state
_state = RUN;
scheduler.EnablePeriodic(&_routine_task);
Expand All @@ -105,83 +107,76 @@ void SpaceshipApp::Routine(void* unused) {

// render current status
_Render();

// move on
// ## 1. move all bullets to right
_MoveBulletsToRight();
// ## 2. generate enemy or move enemy to left
if (_has_enemy) {
_enemy_position -= 1;
} else {
_GenerateEnemy();
_has_enemy = true;
}
_MoveEnemyToLeft();
if (!_has_enemy) _GenerateEnemy();
}

void SpaceshipApp::_SyncMyPlane(uint8_t new_position) {
if (new_position != _my_position) {
_my_position = new_position;
_my_plane[0] = _my_position;
_my_plane[1] = _my_position + 1;
_my_plane[2] = _my_position - DISPLAY_WIDTH;
_my_plane[3] = _my_position + DISPLAY_WIDTH;
_my_plane[0] = _my_position | _my_position << 1 | _my_position >> 1;
_my_plane[1] = _my_position;
}
}

void SpaceshipApp::_Render() {
uint8_t frame_buf[DISPLAY_HEIGHT * DISPLAY_WIDTH] = {0};
// ## 1. render plane
for (uint8_t i = 0; i < 4; i++) {
frame_buf[_my_plane[i]] = 1;
}
// ## 2. render enemy
frame_buf[_enemy_position] = 1;
// ## 3. render bullets
for (uint8_t i = 0; i < _num_bullets; i++) {
frame_buf[_bullets[i]] = 1;
for (uint8_t i = 0; i < DISPLAY_WIDTH; i++) {
_frame_buf[i] = _enemy_position[i] | _bullets[i];
if (i < 2) {
_frame_buf[i] |= _my_plane[i];
}
}
display_set_mode_fixed(frame_buf);
display_set_mode_fixed_packed(_frame_buf);
}

void SpaceshipApp::_CheckCollision() {
for (uint8_t i = 0; i < 4; i++) {
if (_my_plane[i] == _enemy_position) {
for (uint8_t i = 0; i < 2; i++) {
if ((_my_plane[i] & _enemy_position[i]) != 0) {
_state = GAME_OVER; // game over!
break;
}
}
}

void SpaceshipApp::_UpdateScoreBullets() {
uint8_t left_bullets = 0;
for (uint8_t i = 0; i < _num_bullets; i++) {
if (_bullets[i] == _enemy_position) {
for (uint8_t i = 0; i < DISPLAY_WIDTH; i++) {
if ((_bullets[i] & _enemy_position[i]) != 0) {
_score += 1;
_bullets[i] &= ~_enemy_position[i]; // remove bullet
_enemy_position[i] = 0; // remove enemy
_has_enemy = false;
// this bullet is used.
} else {
_bullets[left_bullets] = _bullets[i];
left_bullets += 1;
break;
}
}
_num_bullets = left_bullets; // update _num_bullets
}

void SpaceshipApp::_MoveBulletsToRight() {
uint8_t left_bullets = 0;
for (uint8_t i = 0; i < _num_bullets; i++) {
_bullets[i] += 1;
if (_bullets[i] % DISPLAY_WIDTH > 0) { // check bullet within region
_bullets[left_bullets] = _bullets[i];
left_bullets += 1;
}
// bullets stay in column 2 to column DISPLAY_WIDTH-1
for (uint8_t i = DISPLAY_WIDTH - 1; i >= 2; i--) {
_bullets[i] = _bullets[i - 1];
}
_num_bullets = left_bullets; // update _num_bullets
}

void SpaceshipApp::_GenerateEnemy() {
uint8_t row = g_fast_random_pool.GetRandom() % (DISPLAY_HEIGHT - 2);
_enemy_position = (row + 2) * DISPLAY_WIDTH - 1;
_enemy_position[DISPLAY_WIDTH - 1] = 1 << (row + 1);
_has_enemy = true;
}

void SpaceshipApp::_MoveEnemyToLeft() {
if (!_has_enemy) return;
_has_enemy = false;
for (uint8_t i = 0; i < (DISPLAY_WIDTH - 1); i++) {
_enemy_position[i] = _enemy_position[i + 1];
_enemy_position[i + 1] = 0;
if (_enemy_position[i] > 0) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Best to use != 0 for bitfield checks

_has_enemy = true;
}
}
}

} // namespace spaceship
Expand Down
17 changes: 10 additions & 7 deletions fw/Core/Hitcon/App/SpaceshipApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,29 @@ enum state_t { INIT, RUN, GAME_OVER, END };

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to static_assert() on the display height.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I use

static constexpr unsigned PLANE_UPPER_BOUND = 1 << (DISPLAY_HEIGHT-2);

instead of using

static_assert(DISPLAY_HEIGHT==8)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why you need static_assert() is because you used a byte to represent a column, which is 8 pixels currently. Whenever that changes your code will break, and we'll want to warn anyone of that using static_assert().

class SpaceshipApp : public App {
private:
// interval for bullet and enemy moving
static constexpr unsigned INTERVAL = 350;
static_assert(DISPLAY_HEIGHT) static constexpr unsigned INTERVAL = 350;
static constexpr unsigned PLANE_LOWER_BOUND = 1 << 1;
static constexpr unsigned PLANE_UPPER_BOUND = 1 << (DISPLAY_HEIGHT - 2);

PeriodicTask _routine_task;
uint8_t _my_position = 0;
uint8_t _my_plane[4];
uint8_t _enemy_position;
uint8_t _my_position;
uint8_t _my_plane[2];
uint8_t _enemy_position[DISPLAY_WIDTH];
bool _has_enemy;
uint32_t _score;
uint8_t _state;

// handle bullets
uint8_t _num_bullets;
uint8_t _bullets[DISPLAY_HEIGHT * DISPLAY_WIDTH]; // 0: head
uint8_t _bullets[DISPLAY_WIDTH];
// for display
uint8_t _frame_buf[DISPLAY_WIDTH];

void Routine(void* unused);
void _StartGame();
void _GenerateEnemy();
void _Render();
void _SyncMyPlane(uint8_t new_position);
void _MoveEnemyToLeft();
void _MoveBulletsToRight();
void _CheckCollision();
void _UpdateScoreBullets();
Expand Down