Skip to content

Commit

Permalink
implemented stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
mgerhold committed Jul 30, 2024
1 parent dee8ed6 commit 20d9bea
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 24 deletions.
1 change: 1 addition & 0 deletions src/obpf/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_library(obpf
include/obpf/lobby.h
lobby.cpp
include/obpf/rotation.h
include/obpf/stats.hpp
)

target_compile_definitions(obpf PRIVATE "simulator_EXPORTS")
Expand Down
3 changes: 3 additions & 0 deletions src/obpf/include/obpf/simulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ extern "C" {
#include <stdint.h>
#include "input.h"
#include "rotation.h"
#include "stats.hpp"
#include "tetromino.h"
#include "tetromino_type.h"
#include "vec2.h"
Expand Down Expand Up @@ -48,6 +49,8 @@ extern "C" {
);

OBPF_EXPORT struct ObpfTetrion* obpf_create_tetrion(uint64_t seed);
OBPF_EXPORT ObpfStats obpf_tetrion_get_stats(struct ObpfTetrion const* tetrion);
OBPF_EXPORT bool obpf_tetrion_is_game_over(struct ObpfTetrion const* tetrion);
OBPF_EXPORT ObpfLineClearDelayState obpf_tetrion_get_line_clear_delay_state(struct ObpfTetrion const* tetrion);
OBPF_EXPORT bool obpf_tetrion_try_get_active_tetromino(
struct ObpfTetrion const* tetrion,
Expand Down
17 changes: 17 additions & 0 deletions src/obpf/include/obpf/stats.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>

typedef struct {
uint64_t score;
uint32_t lines_cleared;
uint32_t level;
} ObpfStats;

#ifdef __cplusplus
}
#endif
12 changes: 12 additions & 0 deletions src/obpf/simulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,15 @@ ObpfKeyState obpf_key_state_create(
.get_bitmask(),
};
}

ObpfStats obpf_tetrion_get_stats(ObpfTetrion const* tetrion) {
return ObpfStats{
.score = tetrion->score(),
.lines_cleared = tetrion->num_lines_cleared(),
.level = tetrion->level(),
};
}

bool obpf_tetrion_is_game_over(ObpfTetrion const* const tetrion) {
return tetrion->is_game_over();
}
23 changes: 20 additions & 3 deletions src/simulator/include/simulator/tetrion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ struct ObpfTetrion final {
LockDelayState m_lock_delay_state;
EntryDelay m_entry_delay;
LineClearDelay m_line_clear_delay;
u32 m_lines_cleared = 0;
u32 m_num_lines_cleared = 0;
u64 m_score = 0;
u64 m_next_gravity_frame = gravity_delay_by_level(0); // todo: offset by starting frame given by the server
bool m_is_soft_dropping = false;
bool m_is_game_over = false;

static constexpr u64 gravity_delay_by_level(u32 const level) {
constexpr auto delays = std::array<u64, 13>{
Expand Down Expand Up @@ -81,8 +83,23 @@ struct ObpfTetrion final {
return m_next_frame;
}

[[nodiscard]] u32 level() const;

[[nodiscard]] u64 score() const {
return m_score;
}

[[nodiscard]] u32 num_lines_cleared() const {
return m_num_lines_cleared;
}

[[nodiscard]] bool is_game_over() const {
return m_is_game_over;
}

private:
void freeze_and_destroy_active_tetromino();
[[nodiscard]] bool is_tetromino_completely_invisible(Tetromino const& tetromino) const;
[[nodiscard]] bool is_tetromino_completely_visible(Tetromino const& tetromino) const;
[[nodiscard]] bool is_tetromino_position_valid(Tetromino const& tetromino) const;
[[nodiscard]] bool is_active_tetromino_position_valid() const;
Expand All @@ -96,11 +113,11 @@ struct ObpfTetrion final {
void rotate(RotationDirection direction);
void rotate_clockwise();
void rotate_counter_clockwise();
void drop();
void hard_drop();
void hold();
void determine_lines_to_clear();
[[nodiscard]] u64 score_for_num_lines_cleared(std::size_t num_lines_cleared) const;
void clear_lines(c2k::StaticVector<u8, 4> lines);
[[nodiscard]] u32 level() const;
void refresh_ghost_tetromino();

[[nodiscard]] static std::array<Bag, 2> create_two_bags(c2k::Random& random);
Expand Down
73 changes: 52 additions & 21 deletions src/simulator/tetrion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
}

void ObpfTetrion::simulate_next_frame(KeyState const key_state) {
if (is_game_over()) {
++m_next_frame;
return;
}

auto const line_clear_delay_poll_result = m_line_clear_delay.poll();

if (std::get_if<LineClearDelay::DelayEnded>(&line_clear_delay_poll_result) != nullptr) {
Expand All @@ -46,8 +51,12 @@ void ObpfTetrion::simulate_next_frame(KeyState const key_state) {

switch (m_entry_delay.poll()) {
case EntryDelayPollResult::ShouldSpawn:
spawn_next_tetromino();
spawn_next_tetromino(); // this is where we could possibly have lost the game
m_lock_delay_state.clear();
if (is_game_over()) {
++m_next_frame;
return;
}
break;
case EntryDelayPollResult::ShouldNotSpawn:
break;
Expand All @@ -69,8 +78,10 @@ void ObpfTetrion::simulate_next_frame(KeyState const key_state) {

switch (m_lock_delay_state.poll()) {
case LockDelayPollResult::ShouldLock:
freeze_and_destroy_active_tetromino();
freeze_and_destroy_active_tetromino(); // we could lose the game here due to "Lock Out"
m_is_hold_possible = false;
// Even if we lost the game, it's not an error to start the entry delay -- it will simply get
// ignored at the start of the next frame.
m_entry_delay.start();
break;
case LockDelayPollResult::ShouldNotLock:
Expand Down Expand Up @@ -120,29 +131,35 @@ void ObpfTetrion::simulate_next_frame(KeyState const key_state) {
return m_hold_piece;
}

[[nodiscard]] u32 ObpfTetrion::level() const {
return m_num_lines_cleared / 10;
}

void ObpfTetrion::freeze_and_destroy_active_tetromino() {
if (not active_tetromino().has_value()) {
return;
}
auto const mino_positions = get_mino_positions(active_tetromino().value());
if (is_tetromino_completely_invisible(active_tetromino().value())) {
m_is_game_over = true;
}
for (auto const position : mino_positions) {
m_matrix[position] = active_tetromino().value().type;
}
m_active_tetromino = std::nullopt;
}

[[nodiscard]] bool ObpfTetrion::is_tetromino_completely_visible(Tetromino const& tetromino) const {
if (not is_tetromino_position_valid(tetromino)) {
return false;
}

for (auto const& position : get_mino_positions(tetromino)) {
if (position.y < gsl::narrow<i32>(Matrix::num_invisible_lines)) {
return false;
}
}
bool ObpfTetrion::is_tetromino_completely_invisible(Tetromino const& tetromino) const {
return std::ranges::all_of(get_mino_positions(tetromino), [](auto const position) {
return position.y < gsl::narrow<i32>(Matrix::num_invisible_lines);
});
}

return true;
[[nodiscard]] bool ObpfTetrion::is_tetromino_completely_visible(Tetromino const& tetromino) const {
return is_tetromino_position_valid(tetromino)
and std::ranges::all_of(get_mino_positions(tetromino), [](auto const position) {
return position.y >= gsl::narrow<i32>(Matrix::num_invisible_lines);
});
}

[[nodiscard]] bool ObpfTetrion::is_tetromino_position_valid(Tetromino const& tetromino) const {
Expand Down Expand Up @@ -184,7 +201,11 @@ void ObpfTetrion::spawn_next_tetromino() {
m_is_hold_possible = true;
}

// todo: check game over state here
if (not is_active_tetromino_position_valid()) {
m_is_game_over = true;
m_is_soft_dropping = false;
return;
}

// clang-format off
for (
Expand Down Expand Up @@ -236,7 +257,7 @@ void ObpfTetrion::handle_key_press(Key const key) {
m_next_gravity_frame = m_next_frame;
break;
case Key::Drop:
drop();
hard_drop();
break;
case Key::RotateClockwise:
rotate_clockwise();
Expand Down Expand Up @@ -303,6 +324,9 @@ void ObpfTetrion::move_down(DownMovementType const movement_type) {
++m_active_tetromino.value().position.y;
if (is_active_tetromino_position_valid()) {
m_lock_delay_state.on_tetromino_moved(LockDelayMovementType::MovedDown);
if (movement_type == DownMovementType::SoftDrop) {
++m_score;
}
} else {
--m_active_tetromino.value().position.y;
switch (movement_type) {
Expand Down Expand Up @@ -346,14 +370,19 @@ void ObpfTetrion::rotate_counter_clockwise() {
rotate(RotationDirection::CounterClockwise);
}

void ObpfTetrion::drop() {
void ObpfTetrion::hard_drop() {
if (not active_tetromino().has_value()) {
return;
}
auto num_lines_dropped = std::size_t{ 0 };
do {
++m_active_tetromino.value().position.y;
++num_lines_dropped;
} while (is_active_tetromino_position_valid());
--m_active_tetromino.value().position.y;
--num_lines_dropped;
static constexpr auto score_per_line = u64{ 2 };
m_score += num_lines_dropped * score_per_line;
m_lock_delay_state.on_hard_drop_lock();
}

Expand Down Expand Up @@ -387,7 +416,13 @@ void ObpfTetrion::determine_lines_to_clear() {
}
}

[[nodiscard]] u64 ObpfTetrion::score_for_num_lines_cleared(std::size_t const num_lines_cleared) const {
static constexpr auto score_multipliers = std::array<std::size_t, 5>{ 0, 100, 300, 500, 800 };
return score_multipliers.at(num_lines_cleared) * (level() + 1);
}

void ObpfTetrion::clear_lines(c2k::StaticVector<u8, 4> const lines) {
m_score += score_for_num_lines_cleared(lines.size());
auto num_lines_cleared = decltype(lines.front()){ 0 };
for (auto const line_to_clear : lines) {
for (auto i = decltype(line_to_clear){ 0 }; i < line_to_clear; ++i) {
Expand All @@ -397,11 +432,7 @@ void ObpfTetrion::clear_lines(c2k::StaticVector<u8, 4> const lines) {
++num_lines_cleared;
m_matrix.fill(num_lines_cleared, TetrominoType::Empty);
}
m_lines_cleared += gsl::narrow<decltype(m_lines_cleared)>(lines.size());
}

[[nodiscard]] u32 ObpfTetrion::level() const {
return m_lines_cleared / 10;
m_num_lines_cleared += gsl::narrow<decltype(m_num_lines_cleared)>(lines.size());
}

void ObpfTetrion::refresh_ghost_tetromino() {
Expand Down

0 comments on commit 20d9bea

Please sign in to comment.