Skip to content

Commit

Permalink
Merge pull request #112 from john0312/chiffon-tetris
Browse files Browse the repository at this point in the history
Tetris feature and bug fix
  • Loading branch information
ktpss95112 authored Aug 17, 2024
2 parents 7a17ee0 + 82546cc commit a8bc9d9
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 59 deletions.
163 changes: 108 additions & 55 deletions fw/Core/Hitcon/App/TetrisApp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <App/MainMenuApp.h>
#include <App/ShowNameApp.h>
#include <App/ShowScoreApp.h>
#include <Logic/BadgeController.h>
#include <Logic/Display/display.h>
#include <Logic/RandomPool.h>
Expand Down Expand Up @@ -37,6 +38,7 @@ TetrisApp::TetrisApp()
void TetrisApp::OnEntry() {
// start a new game
game = hitcon::tetris::TetrisGame(tetris_random);
display_set_mode_scroll_text("Ready?");

// start the update task
hitcon::service::sched::scheduler.EnablePeriodic(&periodic_task);
Expand All @@ -54,13 +56,9 @@ void SetSingleplayer() { tetris_app.SetPlayerCount(SINGLEPLAYER); }
void SetMultiplayer() { tetris_app.SetPlayerCount(MULTIPLAYER); }

void TetrisApp::SetPlayerCount(unsigned playerCount) {
switch (playerCount) {
case SINGLEPLAYER:
game.game_register_attack_enemy_callback(nullptr);
break;
case MULTIPLAYER:
game.game_register_attack_enemy_callback(SendAttackEnemyPacket);
break;
multiplayer = (playerCount == MULTIPLAYER);
if (multiplayer) {
game.game_register_attack_enemy_callback(SendAttackEnemyPacket);
}
}

Expand All @@ -77,68 +75,123 @@ void TetrisApp::RecvAttackPacket(PacketCallbackArg *packet) {
void TetrisApp::OnXboardRecv(void *arg) {
PacketCallbackArg *packet = reinterpret_cast<PacketCallbackArg *>(arg);
switch (packet->data[0]) {
case PACKET_GAME_START:
game.game_start_playing();
break;

case PACKET_ATTACK:
RecvAttackPacket(packet);
break;

case PACKET_GAME_OVER:
game.game_force_over();

show_score_app.SetScore(game.game_get_score());
badge_controller.change_app(&show_score_app);
break;

case PACKET_ABORT_GAME:
badge_controller.change_app(&main_menu);
break;
}
}

void TetrisApp::OnButton(button_t button) {
if (game.game_is_over()) {
// exit the app
badge_controller.OnAppEnd(this);

} else {
/**
* Note that we need to rotate the badge by 90 degrees clockwise to play the
* game. Therefore, the button is remapped.
*/
switch (button) {
case BUTTON_LEFT:
game.game_on_input(hitcon::tetris::DIRECTION_UP);
break;

case BUTTON_RIGHT:
game.game_on_input(hitcon::tetris::DIRECTION_DOWN);
break;

case BUTTON_DOWN:
game.game_on_input(hitcon::tetris::DIRECTION_LEFT);
break;

case BUTTON_UP:
game.game_on_input(hitcon::tetris::DIRECTION_RIGHT);
break;

case BUTTON_BACK:
badge_controller.change_app(&main_menu);
break;

case BUTTON_LONG_BACK:
badge_controller.change_app(&show_name_app);
break;

default:
break;
switch (game.game_get_state()) {
case hitcon::tetris::GAME_STATE_WAITING: {
if (multiplayer) {
uint8_t code = PACKET_GAME_START;
g_xboard_logic.QueueDataForTx(&code, 1, TETRIS_RECV_ID);
}
game.game_start_playing();
break;
}

case hitcon::tetris::GAME_STATE_GAME_OVER: {
// after ShowScoreApp is implemented, game over won't be handled here
break;
}

case hitcon::tetris::GAME_STATE_PLAYING: {
/**
* Note that we need to rotate the badge by 90 degrees clockwise to play
* the game. Therefore, the button is remapped.
*/
switch (button) {
case BUTTON_LEFT:
game.game_on_input(hitcon::tetris::DIRECTION_UP);
break;

case BUTTON_RIGHT:
game.game_on_input(hitcon::tetris::DIRECTION_DOWN);
break;

case BUTTON_DOWN:
game.game_on_input(hitcon::tetris::DIRECTION_LEFT);
break;

case BUTTON_UP:
game.game_on_input(hitcon::tetris::DIRECTION_RIGHT);
break;

case BUTTON_OK:
game.game_on_input(hitcon::tetris::DIRECTION_FAST_DOWN);
break;

case BUTTON_BACK:
if (multiplayer) {
uint8_t code = PACKET_ABORT_GAME;
g_xboard_logic.QueueDataForTx(&code, 1, TETRIS_RECV_ID);
}
badge_controller.change_app(&main_menu);
break;

case BUTTON_LONG_BACK:
if (multiplayer) {
uint8_t code = PACKET_ABORT_GAME;
g_xboard_logic.QueueDataForTx(&code, 1, TETRIS_RECV_ID);
}
badge_controller.change_app(&show_name_app);
break;

default:
break;
}
}
}
}

void TetrisApp::periodic_task_callback(void *) {
if (game.game_is_over()) {
// TODO: maybe score?
} else {
// check if it's time to fall down
unsigned now = SysTimer::GetTime();
if (now - last_fall_time >= hitcon::tetris::FALL_PERIOD) {
game.game_fall_down_tetromino();
last_fall_time = now;
switch (game.game_get_state()) {
case hitcon::tetris::GAME_STATE_WAITING: {
break;
}

// update display buffer
display_buf_t display_buf[DISPLAY_WIDTH];
game.game_draw_to_display(display_buf);
display_set_mode_fixed_packed(display_buf);
case hitcon::tetris::GAME_STATE_GAME_OVER: {
if (multiplayer) {
uint8_t code = PACKET_GAME_OVER;
g_xboard_logic.QueueDataForTx(&code, 1, TETRIS_RECV_ID);
}

show_score_app.SetScore(game.game_get_score());
badge_controller.change_app(&show_score_app);
break;
}

case hitcon::tetris::GAME_STATE_PLAYING: {
// check if it's time to fall down
unsigned now = SysTimer::GetTime();
if (now - last_fall_time >= hitcon::tetris::FALL_PERIOD) {
game.game_fall_down_tetromino();
last_fall_time = now;
}

// update display buffer
display_buf_t display_buf[DISPLAY_WIDTH];
game.game_draw_to_display(display_buf);
display_set_mode_fixed_packed(display_buf);
break;
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions fw/Core/Hitcon/App/TetrisApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ enum { // XBOARD
PACKET_ATTACK = 1,
PACKET_GAME_START,
PACKET_GAME_OVER,
PACKET_ABORT_GAME,
};

enum { // player count
Expand All @@ -42,6 +43,8 @@ void SetMultiplayer();
*/
class TetrisApp : public App {
private:
bool multiplayer = false;

hitcon::tetris::TetrisGame game;
hitcon::service::sched::PeriodicTask periodic_task;

Expand Down
9 changes: 9 additions & 0 deletions fw/Core/Hitcon/App/TetrisGame.cc
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,15 @@ void TetrisGame::game_on_input(TetrisDirection direction) {
}
break;

case DIRECTION_FAST_DOWN:
unplace_tetromino(current_x, current_y);
while (place_tetromino(current_x, current_y + 1)) {
unplace_tetromino(current_x, current_y);
}
place_tetromino(current_x, current_y);
fall_down_tetromino();
break;

default:
break;
}
Expand Down
8 changes: 6 additions & 2 deletions fw/Core/Hitcon/App/TetrisGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static_assert(BOARD_HEIGHT == DISPLAY_WIDTH,
"BOARD_HEIGHT must be equal to DISPLAY_WIDTH");

enum TetrisGameState {
GAME_STATE_WAITING,
GAME_STATE_PLAYING,
GAME_STATE_GAME_OVER,
};
Expand All @@ -28,6 +29,7 @@ enum TetrisDirection {
DIRECTION_LEFT,
DIRECTION_RIGHT,
DIRECTION_DOWN,
DIRECTION_FAST_DOWN,
};

// clang-format off
Expand Down Expand Up @@ -101,7 +103,7 @@ const struct tetromino_t {
*/
class TetrisGame {
private:
TetrisGameState state = GAME_STATE_PLAYING;
TetrisGameState state = GAME_STATE_WAITING;
unsigned last_fall_time = 0;
int current_tetromino;
int current_x;
Expand Down Expand Up @@ -137,7 +139,9 @@ class TetrisGame {
void game_fall_down_tetromino();
void game_on_input(TetrisDirection direction);
void game_draw_to_display(display_buf_t *buf);
inline bool game_is_over() const { return state == GAME_STATE_GAME_OVER; };
inline void game_start_playing() { state = GAME_STATE_PLAYING; }
inline void game_force_over() { game_over(); }
inline TetrisGameState game_get_state() const { return state; };
inline int game_get_score() const { return score; }

// 2-player game, this function should be called when enemy attacks us.
Expand Down
11 changes: 9 additions & 2 deletions fw/Core/Hitcon/App/test-tetris.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,14 @@ void gameFunction() {
.count();
};
auto prev_update = now_ms() - hitcon::tetris::FALL_PERIOD;
game.game_start_playing();
while (1) {
auto now = now_ms();
if (now - prev_update >= hitcon::tetris::FALL_PERIOD) {
std::lock_guard<std::mutex> lock(game_mutex);
game.game_fall_down_tetromino();
prev_update = now;
if (game.game_is_over()) {
if (game.game_get_state() == hitcon::tetris::GAME_STATE_GAME_OVER) {
break;
}
}
Expand All @@ -83,7 +84,7 @@ void ioFunction() {
while (1) {
{
std::lock_guard<std::mutex> lock(game_mutex);
if (game.game_is_over()) {
if (game.game_get_state() == hitcon::tetris::GAME_STATE_GAME_OVER) {
break;
}
}
Expand Down Expand Up @@ -121,6 +122,12 @@ void ioFunction() {
break;
}

case 'f': {
std::lock_guard<std::mutex> lock(game_mutex);
game.game_on_input(hitcon::tetris::DIRECTION_FAST_DOWN);
break;
}

default:
break;
}
Expand Down

0 comments on commit a8bc9d9

Please sign in to comment.