initial monopoly test
This commit is contained in:
@@ -45,6 +45,8 @@ add_executable(basic1
|
|||||||
lib/game_launcher.cpp
|
lib/game_launcher.cpp
|
||||||
games/tic_tac_toe.cpp
|
games/tic_tac_toe.cpp
|
||||||
games/demo_game.cpp
|
games/demo_game.cpp
|
||||||
|
games/monopoly/monopoly_game.cpp
|
||||||
|
games/monopoly/player.c
|
||||||
lib/st7796/st7796.c
|
lib/st7796/st7796.c
|
||||||
lib/ft6336u/ft6336u.c
|
lib/ft6336u/ft6336u.c
|
||||||
lib/sd_card/sd_card.c
|
lib/sd_card/sd_card.c
|
||||||
@@ -80,6 +82,7 @@ target_link_libraries(basic1
|
|||||||
target_include_directories(basic1 PRIVATE
|
target_include_directories(basic1 PRIVATE
|
||||||
${CMAKE_CURRENT_LIST_DIR}
|
${CMAKE_CURRENT_LIST_DIR}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/games
|
${CMAKE_CURRENT_LIST_DIR}/games
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/games/monopoly
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib
|
${CMAKE_CURRENT_LIST_DIR}/lib
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/fatfs/source
|
${CMAKE_CURRENT_LIST_DIR}/lib/fatfs/source
|
||||||
${CMAKE_CURRENT_LIST_DIR}/lib/st7796
|
${CMAKE_CURRENT_LIST_DIR}/lib/st7796
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#include "game_launcher.h"
|
#include "game_launcher.h"
|
||||||
#include "tic_tac_toe.h"
|
#include "tic_tac_toe.h"
|
||||||
#include "demo_game.h"
|
#include "demo_game.h"
|
||||||
|
#include "monopoly_game.h"
|
||||||
|
|
||||||
|
|
||||||
// Binary info for RP2350 - ensures proper boot image structure
|
// Binary info for RP2350 - ensures proper boot image structure
|
||||||
@@ -349,6 +350,11 @@ int main()
|
|||||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
return new TicTacToeGame(w, h, r, g, im);
|
return new TicTacToeGame(w, h, r, g, im);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
launcher.register_game("Monopoly", "Classic property trading game",
|
||||||
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
|
return new MonopolyGame(w, h, r, g, im);
|
||||||
|
});
|
||||||
|
|
||||||
launcher.register_game("Demo Game", "Simple test game",
|
launcher.register_game("Demo Game", "Simple test game",
|
||||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// ---- SELECT YOUR BOARD HERE ----
|
// ---- SELECT YOUR BOARD HERE ----
|
||||||
#define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796
|
// #define BOARD_FEATHER_TFT // Feather RP2350 + 4.0" TFT ST7796
|
||||||
// #define BOARD_PICO2_TFT // Pico 2 + 4.0" TFT ST7796
|
// #define BOARD_PICO2_TFT // Pico 2 + 4.0" TFT ST7796
|
||||||
// #define BOARD_PICO2_EINK // Pico 2 + E-Ink Display
|
#define BOARD_PICO2_EINK // Pico 2 + E-Ink Display
|
||||||
// --------------------------------
|
// --------------------------------
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ class LowLevelGUI
|
|||||||
private:
|
private:
|
||||||
LowLevelRenderer* renderer;
|
LowLevelRenderer* renderer;
|
||||||
bool use_rounded_corners = true;
|
bool use_rounded_corners = true;
|
||||||
const Font* current_font;
|
|
||||||
public:
|
public:
|
||||||
|
const Font* current_font;
|
||||||
LowLevelGUI(LowLevelRenderer* rend, const Font& font);
|
LowLevelGUI(LowLevelRenderer* rend, const Font& font);
|
||||||
LowLevelWindow* draw_new_window(int x, int y, int width, int height, const char *title);
|
LowLevelWindow* draw_new_window(int x, int y, int width, int height, const char *title);
|
||||||
void draw_window(LowLevelWindow* window);
|
void draw_window(LowLevelWindow* window);
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ SRC = main.cpp low_level_display_sfml.cpp input_manager.cpp game_launcher.cpp \
|
|||||||
../display/low_level_render.cpp \
|
../display/low_level_render.cpp \
|
||||||
../display/low_level_gui.cpp \
|
../display/low_level_gui.cpp \
|
||||||
../games/demo_game.cpp \
|
../games/demo_game.cpp \
|
||||||
../games/tic_tac_toe.cpp
|
../games/tic_tac_toe.cpp \
|
||||||
|
../games/monopoly/monopoly_game.cpp \
|
||||||
|
../games/monopoly/player.c
|
||||||
|
|
||||||
$(TARGET): $(SRC)
|
$(TARGET): $(SRC)
|
||||||
$(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET) $(INCLUDES) $(LIBS)
|
$(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET) $(INCLUDES) $(LIBS)
|
||||||
|
|||||||
Binary file not shown.
@@ -5,6 +5,7 @@
|
|||||||
#include "game_launcher.h"
|
#include "game_launcher.h"
|
||||||
#include "../games/demo_game.h"
|
#include "../games/demo_game.h"
|
||||||
#include "../games/tic_tac_toe.h"
|
#include "../games/tic_tac_toe.h"
|
||||||
|
#include "../games/monopoly/monopoly_game.h"
|
||||||
#include "input_manager.h"
|
#include "input_manager.h"
|
||||||
#include <SFML/Window.hpp>
|
#include <SFML/Window.hpp>
|
||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
@@ -37,6 +38,10 @@ int main() {
|
|||||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
return new TicTacToeGame(w, h, r, g, im);
|
return new TicTacToeGame(w, h, r, g, im);
|
||||||
});
|
});
|
||||||
|
launcher.register_game("Monopoly", "Classic property trading game",
|
||||||
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
|
return new MonopolyGame(w, h, r, g, im);
|
||||||
|
});
|
||||||
launcher.register_game("Demo Game", "Simple test game",
|
launcher.register_game("Demo Game", "Simple test game",
|
||||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||||
return new DemoGame(w, h, r, g, im);
|
return new DemoGame(w, h, r, g, im);
|
||||||
|
|||||||
56
games/monopoly/chance.h
Normal file
56
games/monopoly/chance.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#ifndef CHANCE_H
|
||||||
|
#define CHANCE_H
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CHANCE_ADVANCE, // Move to a specific index or category
|
||||||
|
CHANCE_EARN, // Add money
|
||||||
|
CHANCE_SPEND, // Subtract money
|
||||||
|
CHANCE_SPEND_EACH_PLAYER, // Pay every other player
|
||||||
|
CHANCE_BACK, // Move back X spaces
|
||||||
|
CHANCE_JAIL, // Go to jail
|
||||||
|
CHANCE_JAIL_FREE, // Keep a get out of jail free card
|
||||||
|
CHANCE_REPAIRS // Pay per house/hotel
|
||||||
|
} ChanceType;
|
||||||
|
|
||||||
|
// Sentinel values for "nearest" targets
|
||||||
|
#define TARGET_NEAREST_UTILITY -1
|
||||||
|
#define TARGET_NEAREST_RAILROAD -2
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* description;
|
||||||
|
ChanceType type;
|
||||||
|
int value; // Used for index, amount, or spaces back
|
||||||
|
int value2; // Specifically for repairs (Hotel cost)
|
||||||
|
} ChanceCard;
|
||||||
|
|
||||||
|
#define CHANCE_DECK_SIZE 16
|
||||||
|
|
||||||
|
static const ChanceCard CHANCE_DECK[CHANCE_DECK_SIZE] = {
|
||||||
|
{"Advance to Go", CHANCE_ADVANCE, 0, 0},
|
||||||
|
{"Advance to Illinois Ave", CHANCE_ADVANCE, 24, 0},
|
||||||
|
{"Advance to nearest Utility. If unowned, buy it. If owned, pay 10x dice.", CHANCE_ADVANCE, TARGET_NEAREST_UTILITY, 0},
|
||||||
|
{"Advance to nearest Railroad. If unowned, buy it. If owned, pay twice rental.", CHANCE_ADVANCE, TARGET_NEAREST_RAILROAD, 0},
|
||||||
|
{"Advance to St. Charles Place – if you pass Go, collect $200", CHANCE_ADVANCE, 11, 0},
|
||||||
|
{"Bank pays you dividend of $50", CHANCE_EARN, 50, 0},
|
||||||
|
{"Get out of Jail free", CHANCE_JAIL_FREE, 0, 0},
|
||||||
|
{"Go back 3 spaces", CHANCE_BACK, 3, 0},
|
||||||
|
{"Go directly to Jail", CHANCE_JAIL, 0, 0},
|
||||||
|
{"Make general repairs - $25 per house, $100 per hotel", CHANCE_REPAIRS, 25, 100},
|
||||||
|
{"Pay poor tax of $15", CHANCE_SPEND, 15, 0},
|
||||||
|
{"Take a trip to Reading Railroad", CHANCE_ADVANCE, 5, 0},
|
||||||
|
{"Take a walk on the Boardwalk", CHANCE_ADVANCE, 39, 0},
|
||||||
|
{"Elected chairman of the board – pay each player $50", CHANCE_SPEND_EACH_PLAYER, 50, 0},
|
||||||
|
{"Your building loan matures – collect $150", CHANCE_EARN, 150, 0},
|
||||||
|
{"You have won a crossword competition - collect $100", CHANCE_EARN, 100, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation Tips for your main.c:
|
||||||
|
Handling "Nearest": When you draw a card with TARGET_NEAREST_UTILITY, use a loop starting from the player's current position to find the next index where MONOPOLY_BOARD[i].type == TILE_UTILITY.
|
||||||
|
|
||||||
|
The Repair Card: When the type is CHANCE_REPAIRS, you'll need to look at the player's owned properties and count how many houses/hotels they have (once you implement the building logic), then multiply by value (25) and value2 (100).
|
||||||
|
|
||||||
|
Shuffling: In C, you can shuffle the deck at the start of the game using a Fisher-Yates shuffle algorithm on an array of indices [0...15].
|
||||||
|
*/
|
||||||
43
games/monopoly/community_chest.h
Normal file
43
games/monopoly/community_chest.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef COMMUNITY_H
|
||||||
|
#define COMMUNITY_H
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
COMMUNITY_ADVANCE,
|
||||||
|
COMMUNITY_EARN,
|
||||||
|
COMMUNITY_SPEND,
|
||||||
|
COMMUNITY_EARN_EACH_PLAYER,
|
||||||
|
COMMUNITY_JAIL,
|
||||||
|
COMMUNITY_JAIL_FREE,
|
||||||
|
COMMUNITY_REPAIRS
|
||||||
|
} CommunityType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* description;
|
||||||
|
CommunityType type;
|
||||||
|
int value; // Amount or position
|
||||||
|
int value2; // Second repair cost (Hotels)
|
||||||
|
} CommunityCard;
|
||||||
|
|
||||||
|
#define COMMUNITY_DECK_SIZE 17
|
||||||
|
|
||||||
|
static const CommunityCard COMMUNITY_DECK[COMMUNITY_DECK_SIZE] = {
|
||||||
|
{"Advance to Go (Collect $200)", COMMUNITY_ADVANCE, 0, 0},
|
||||||
|
{"Bank error in your favor – collect $75", COMMUNITY_EARN, 75, 0},
|
||||||
|
{"Doctor's fees – Pay $50", COMMUNITY_SPEND, 50, 0},
|
||||||
|
{"Get out of jail free", COMMUNITY_JAIL_FREE, 0, 0},
|
||||||
|
{"Go to jail – Do not pass Go, do not collect $200", COMMUNITY_JAIL, 0, 0},
|
||||||
|
{"It is your birthday - Collect $10 from each player", COMMUNITY_EARN_EACH_PLAYER, 10, 0},
|
||||||
|
{"Grand Opera Night – collect $50 from every player", COMMUNITY_EARN_EACH_PLAYER, 50, 0},
|
||||||
|
{"Income Tax refund – collect $20", COMMUNITY_EARN, 20, 0},
|
||||||
|
{"Life Insurance Matures – collect $100", COMMUNITY_EARN, 100, 0},
|
||||||
|
{"Pay Hospital Fees of $100", COMMUNITY_SPEND, 100, 0},
|
||||||
|
{"Receive $25 Consultancy Fee", COMMUNITY_EARN, 25, 0},
|
||||||
|
{"Pay School Fees of $50", COMMUNITY_SPEND, 50, 0},
|
||||||
|
{"Street repairs – $40 per house, $115 per hotel", COMMUNITY_REPAIRS, 40, 115},
|
||||||
|
{"Won second prize in a beauty contest – collect $10", COMMUNITY_EARN, 10, 0},
|
||||||
|
{"You inherit $100", COMMUNITY_EARN, 100, 0},
|
||||||
|
{"From sale of stock you get $50", COMMUNITY_EARN, 50, 0},
|
||||||
|
{"Holiday Fund matures - Receive $100", COMMUNITY_EARN, 100, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
632
games/monopoly/monopoly.cpp
Normal file
632
games/monopoly/monopoly.cpp
Normal file
@@ -0,0 +1,632 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include "monopoly_board.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "chance.h"
|
||||||
|
#include "community_chest.h"
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
void handle_roll(Player *p, bool *has_rolled, int *double_rolls);
|
||||||
|
void handle_buy(Player *p, bool has_rolled);
|
||||||
|
void handle_trade(Player *p);
|
||||||
|
void handle_build(Player *p);
|
||||||
|
void process_landing(Player *p);
|
||||||
|
void handle_property_landing(Player *p, const BoardTile *tile);
|
||||||
|
void handle_chance(Player* p);
|
||||||
|
void find_properties_on_group_from_position(int position, int positions_found[4], int *count);
|
||||||
|
void handle_community_chest(Player* p);
|
||||||
|
void handle_jail_options(Player *p);
|
||||||
|
bool attempt_jail_escape(Player *p);
|
||||||
|
|
||||||
|
// Global State
|
||||||
|
Player players[MAX_PLAYERS];
|
||||||
|
int players_count = 2;
|
||||||
|
|
||||||
|
int current_player_idx = 0;
|
||||||
|
bool just_sent_to_jail = false; // Flag to end turn when sent to jail
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
srand(time(NULL));
|
||||||
|
|
||||||
|
// Initialize hardcoded players
|
||||||
|
init_player(&players[0], 0, "Elias", "Top Hat");
|
||||||
|
init_player(&players[1], 1, "Adolfo", "Racecar");
|
||||||
|
init_player(&players[2], 2, "Grace", "Thimble");
|
||||||
|
init_player(&players[3], 3, "Alicia", "Dog");
|
||||||
|
|
||||||
|
printf("\n╔════════════════════════════════════════╗\n");
|
||||||
|
printf("║ Welcome to C-Monopoly! ║\n");
|
||||||
|
printf("╚════════════════════════════════════════╝\n\n");
|
||||||
|
|
||||||
|
bool running = true;
|
||||||
|
bool has_rolled = false; // Turn state tracker
|
||||||
|
int double_rolls = 0;
|
||||||
|
current_player_idx = 0;
|
||||||
|
while (running)
|
||||||
|
{
|
||||||
|
Player *p = &players[current_player_idx];
|
||||||
|
printf("\n╔═════════════════════════════════════════╗\n");
|
||||||
|
printf("║ %s's Turn (%s)\n", p->name, p->token);
|
||||||
|
printf("║ 💰 Balance: $%d | 📍 %s\n", p->balance, MONOPOLY_BOARD[p->position].name);
|
||||||
|
printf("╚═════════════════════════════════════════╝\n\n");
|
||||||
|
// Handle jail status at start of turn
|
||||||
|
if (p->is_in_jail)
|
||||||
|
{
|
||||||
|
if (just_sent_to_jail)
|
||||||
|
{
|
||||||
|
// Player was just sent to jail this turn, end their turn
|
||||||
|
printf("\n🚨 You have been sent to Jail! Your turn ends.\n");
|
||||||
|
current_player_idx = (current_player_idx + 1) % players_count;
|
||||||
|
has_rolled = false;
|
||||||
|
just_sent_to_jail = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n🚨 YOU ARE IN JAIL!\n");
|
||||||
|
printf(" Turns in jail: %d/3\n", p->jail_turns);
|
||||||
|
handle_jail_options(p);
|
||||||
|
|
||||||
|
if (!p->is_in_jail)
|
||||||
|
{
|
||||||
|
// Player escaped jail, now they can roll
|
||||||
|
printf("\n📋 %s escaped jail!\n\n", p->name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Player remains in jail, end turn
|
||||||
|
printf("\n📋 %s remains in jail.\n", p->name);
|
||||||
|
current_player_idx = (current_player_idx + 1) % players_count;
|
||||||
|
has_rolled = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!has_rolled)
|
||||||
|
printf("⚠️ You must roll the dice first!\n\n");
|
||||||
|
|
||||||
|
printf("╔─ Available Actions ──────────────────────╗\n");
|
||||||
|
printf("║ 1. 🎲 Roll Dice\n");
|
||||||
|
printf("║ 2. 🏠 Buy Property\n");
|
||||||
|
printf("║ 3. 🏗️ Build Houses/Hotels\n");
|
||||||
|
printf("║ 4. 🤝 Trade (Empty)\n");
|
||||||
|
printf("║ 5. ⏭️ End Turn\n");
|
||||||
|
printf("║ 6. ❌ Exit Game\n");
|
||||||
|
printf("╚═════════════════════════════════════════╝\n");
|
||||||
|
printf("\nChoose action (1-6): ");
|
||||||
|
|
||||||
|
int choice;
|
||||||
|
scanf("%d", &choice);
|
||||||
|
|
||||||
|
switch (choice)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
handle_roll(p, &has_rolled, &double_rolls);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
handle_buy(p, has_rolled);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
handle_build(p);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
handle_trade(p);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
if (has_rolled)
|
||||||
|
{
|
||||||
|
current_player_idx = (current_player_idx + 1) % players_count;
|
||||||
|
has_rolled = false; // Reset for next player
|
||||||
|
just_sent_to_jail = false; // Reset jail flag
|
||||||
|
printf("✓ Turn ended.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("❌ You must roll before ending your turn!\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("❌ Invalid choice! Please enter 1-6.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void find_properties_on_group_from_position(int position, int positions_found[4], int *count)
|
||||||
|
{
|
||||||
|
const BoardTile *tile = &MONOPOLY_BOARD[position];
|
||||||
|
if (tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD || tile->type == TILE_UTILITY)
|
||||||
|
{
|
||||||
|
int group_id = tile->group[0];
|
||||||
|
*count = 0;
|
||||||
|
for (int i = 0; i < BOARD_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (i == position)
|
||||||
|
continue;
|
||||||
|
const BoardTile *t = &MONOPOLY_BOARD[i];
|
||||||
|
if (t->group[0] == group_id)
|
||||||
|
{
|
||||||
|
positions_found[(*count)++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle_if_owned(Player *p, const BoardTile *tile){
|
||||||
|
if (tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD || tile->type == TILE_UTILITY){
|
||||||
|
bool is_owned = false;
|
||||||
|
int i = 0;
|
||||||
|
for (i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < players[i].property_count; j++)
|
||||||
|
{
|
||||||
|
if (players[i].properties_owned[j] == p->position)
|
||||||
|
{
|
||||||
|
is_owned = true;
|
||||||
|
printf("🏠 This property is owned by %s.\n", players[i].name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_owned)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(is_owned)
|
||||||
|
{
|
||||||
|
if(players[i].id == p->id)
|
||||||
|
{
|
||||||
|
printf("✓ You own this property.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// pay rent logic would go here
|
||||||
|
p->balance -= tile->rent[0]; // Simplistic: always pay base rent
|
||||||
|
players[i].balance += tile->rent[0];
|
||||||
|
printf("💰 Paid $%d in rent to the owner.\n", tile->rent[0]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_roll(Player *p, bool *has_rolled, int *double_rolls)
|
||||||
|
{
|
||||||
|
if (*has_rolled)
|
||||||
|
{
|
||||||
|
printf("❌ You have already moved this turn!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if player is in jail and trying to escape
|
||||||
|
if (p->is_in_jail)
|
||||||
|
{
|
||||||
|
printf("❌ You are in jail! Use the jail menu to escape first.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dice1 = (rand() % 6) + 1;
|
||||||
|
int dice2 = (rand() % 6) + 1;
|
||||||
|
printf("\n🎲 Rolled: [%d] + [%d] = %d\n", dice1, dice2, dice1 + dice2);
|
||||||
|
int total = dice1 + dice2;
|
||||||
|
p->position = (p->position + total) % BOARD_SIZE;
|
||||||
|
|
||||||
|
if(dice1 == dice2)
|
||||||
|
{
|
||||||
|
(*double_rolls)++;
|
||||||
|
if(*double_rolls >= 3)
|
||||||
|
{
|
||||||
|
printf("🚨 Three doubles in a row! Sent directly to JAIL!\n");
|
||||||
|
p->position = 10; // Jail position
|
||||||
|
p->jail_turns = 0;
|
||||||
|
p->is_in_jail = true;
|
||||||
|
*double_rolls = 0;
|
||||||
|
*has_rolled = true;
|
||||||
|
just_sent_to_jail = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("✨ Doubles! You get another turn!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*double_rolls = 0; // Reset double rolls count
|
||||||
|
}
|
||||||
|
|
||||||
|
*has_rolled = true;
|
||||||
|
|
||||||
|
// Check for passing GO
|
||||||
|
if (p->position < (p->position - total))
|
||||||
|
{ // Simplistic wrap-around check
|
||||||
|
p->balance += 200;
|
||||||
|
printf("Passed GO! Collected $200.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("➜ Moved %d spaces to: %s\n\n", total, MONOPOLY_BOARD[p->position].name);
|
||||||
|
process_landing(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_buy(Player *p, bool has_rolled)
|
||||||
|
{
|
||||||
|
if (!has_rolled)
|
||||||
|
{
|
||||||
|
printf("❌ You can't buy anything until you roll and land on a tile!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BoardTile *tile = &MONOPOLY_BOARD[p->position];
|
||||||
|
|
||||||
|
// Check if it's even a purchasable type
|
||||||
|
if (tile->type != TILE_PROPERTY && tile->type != TILE_RAILROAD && tile->type != TILE_UTILITY)
|
||||||
|
{
|
||||||
|
printf("❌ This location (%s) cannot be purchased.\n", tile->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if someone already owns it (basic check)
|
||||||
|
// In a full game, we'd iterate through all players' properties_owned arrays
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i++)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < players[i].property_count; j++)
|
||||||
|
{
|
||||||
|
if (players[i].properties_owned[j] == p->position)
|
||||||
|
{
|
||||||
|
printf("❌ This property is already owned by %s.\n", players[i].name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->balance >= tile->cost)
|
||||||
|
{
|
||||||
|
p->balance -= tile->cost;
|
||||||
|
p->properties_owned[p->property_count++] = p->position;
|
||||||
|
printf("✓ Bought %s for $%d! (Balance: $%d)\n", tile->name, tile->cost, p->balance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("❌ Insufficient funds! Cost: $%d | Your Balance: $%d\n", tile->cost, p->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_landing(Player *p)
|
||||||
|
{
|
||||||
|
const BoardTile *tile = &MONOPOLY_BOARD[p->position];
|
||||||
|
|
||||||
|
printf("\n─────────────────────────────────────────\n");
|
||||||
|
printf("📍 Landed on: %s\n", tile->name);
|
||||||
|
printf("─────────────────────────────────────────\n");
|
||||||
|
|
||||||
|
switch (tile->type)
|
||||||
|
{
|
||||||
|
case TILE_PROPERTY:
|
||||||
|
case TILE_RAILROAD:
|
||||||
|
case TILE_UTILITY:
|
||||||
|
handle_property_landing(p, tile);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TILE_TAX:
|
||||||
|
p->balance -= tile->cost;
|
||||||
|
printf("💸 Tax payment: $%d (Balance: $%d)\n", tile->cost, p->balance);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TILE_CHANCE:
|
||||||
|
printf("\n⚡ CHANCE CARD! ⚡\n");
|
||||||
|
handle_chance(p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TILE_COMMUNITY_CHEST:
|
||||||
|
printf("\n📦 COMMUNITY CHEST CARD! 📦\n");
|
||||||
|
handle_community_chest(p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TILE_FREE_PARKING:
|
||||||
|
case TILE_GO:
|
||||||
|
case TILE_JAIL:
|
||||||
|
// Corner tiles (GO, Free Parking, Go to Jail, Just Visiting)
|
||||||
|
printf("📌 You're at: %s\n", tile->name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_property_landing(Player *p, const BoardTile *tile)
|
||||||
|
{
|
||||||
|
bool is_owned = handle_if_owned(p, tile);
|
||||||
|
if (is_owned)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n💰 Purchase Price: $%d\n", tile->cost);
|
||||||
|
if (tile->type == TILE_PROPERTY)
|
||||||
|
{
|
||||||
|
// rent data
|
||||||
|
printf("\n📋 Rent Schedule:\n");
|
||||||
|
printf(" Vacant.....................$%d\n", tile->rent[0]);
|
||||||
|
printf(" 1 House.....................$%d\n", tile->rent[1]);
|
||||||
|
printf(" 2 Houses.....................$%d\n", tile->rent[2]);
|
||||||
|
printf(" 3 Houses.....................$%d\n", tile->rent[3]);
|
||||||
|
printf(" 4 Houses.....................$%d\n", tile->rent[4]);
|
||||||
|
printf(" Hotel.......................$%d\n", tile->rent[5]);
|
||||||
|
printf("\n🏗️ House Cost: $%d\n", tile->house_cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
// show if the other properties in the group are owned
|
||||||
|
printf("\n🏘️ Property Group: %d | Position %d/%d in group\n",
|
||||||
|
tile->group[0], tile->group[1], tile->group[2]);
|
||||||
|
int group_positions[4];
|
||||||
|
int group_count = 0;
|
||||||
|
find_properties_on_group_from_position(p->position, group_positions, &group_count);
|
||||||
|
if (group_count > 0)
|
||||||
|
{
|
||||||
|
printf("\n Related properties:\n");
|
||||||
|
for (int i = 0; i < group_count; i++)
|
||||||
|
{
|
||||||
|
const BoardTile *gtile = &MONOPOLY_BOARD[group_positions[i]];
|
||||||
|
// Check if owned by any player
|
||||||
|
bool is_owned_in_group = false;
|
||||||
|
for (int j = 0; j < MAX_PLAYERS; j++)
|
||||||
|
{
|
||||||
|
for (int k = 0; k < players[j].property_count; k++)
|
||||||
|
{
|
||||||
|
if (players[j].properties_owned[k] == group_positions[i])
|
||||||
|
{
|
||||||
|
is_owned_in_group = true;
|
||||||
|
printf(" 🔒 %s (Owned by %s)\n", gtile->name, players[j].name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_owned_in_group)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!is_owned_in_group)
|
||||||
|
{
|
||||||
|
printf(" 🔓 %s - $%d (available)\n", gtile->name, gtile->cost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_trade(Player *p)
|
||||||
|
{
|
||||||
|
printf("⚙️ Trade functionality is not yet implemented.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_jail_options(Player *p)
|
||||||
|
{
|
||||||
|
bool escaped = false;
|
||||||
|
|
||||||
|
printf("\n╔─ Jail Options ──────────────────────────────╗\n");
|
||||||
|
printf("║ 1. 🎲 Roll Doubles to Escape\n");
|
||||||
|
printf("║ 2. 💰 Pay $50 Bail\n");
|
||||||
|
if (p->jail_free_cards > 0)
|
||||||
|
printf("║ 3. 🔑 Use Get Out of Jail Free Card\n");
|
||||||
|
printf("╚─────────────────────────────────────────────╝\n");
|
||||||
|
printf("Choose option: ");
|
||||||
|
|
||||||
|
int choice;
|
||||||
|
scanf("%d", &choice);
|
||||||
|
|
||||||
|
switch(choice)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
// Try to roll doubles
|
||||||
|
escaped = attempt_jail_escape(p);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
// Pay bail
|
||||||
|
if (p->balance >= 50)
|
||||||
|
{
|
||||||
|
p->balance -= 50;
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
escaped = true;
|
||||||
|
printf("\n✓ Paid $50 bail! You are now free.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("\n❌ Insufficient funds! You need $50.\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (p->jail_free_cards > 0)
|
||||||
|
{
|
||||||
|
p->jail_free_cards--;
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
escaped = true;
|
||||||
|
printf("\n✓ Used a Get Out of Jail Free card! You are now free.\n");
|
||||||
|
printf(" Cards remaining: %d\n", p->jail_free_cards);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("\n❌ You don't have any Get Out of Jail Free cards!\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("\n❌ Invalid choice!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!escaped)
|
||||||
|
{
|
||||||
|
p->jail_turns++;
|
||||||
|
if (p->jail_turns >= 3)
|
||||||
|
{
|
||||||
|
// Force payment after 3 turns
|
||||||
|
printf("\n⏰ You've been in jail for 3 turns. You must pay $50 bail!\n");
|
||||||
|
p->balance -= 50;
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
printf(" Balance: $%d\n", p->balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool attempt_jail_escape(Player *p)
|
||||||
|
{
|
||||||
|
int dice1 = (rand() % 6) + 1;
|
||||||
|
int dice2 = (rand() % 6) + 1;
|
||||||
|
printf("\n🎲 Rolling to escape...\n");
|
||||||
|
printf(" Rolled: [%d] + [%d]\n", dice1, dice2);
|
||||||
|
|
||||||
|
if (dice1 == dice2)
|
||||||
|
{
|
||||||
|
printf("\n✨ DOUBLES! You escaped jail!\n");
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("\n❌ No doubles. You remain in jail.\n");
|
||||||
|
p->jail_turns++;
|
||||||
|
if (p->jail_turns >= 3)
|
||||||
|
{
|
||||||
|
printf("\n⏰ You've been in jail for 3 turns. You must pay $50 bail!\n");
|
||||||
|
p->balance -= 50;
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
printf(" Balance: $%d\n", p->balance);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_build(Player *p)
|
||||||
|
{
|
||||||
|
printf("⚙️ Build functionality is not yet implemented.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_chance(Player* p) {
|
||||||
|
// In a real game, you'd pull from a shuffled deck of indices
|
||||||
|
int card_idx = rand() % CHANCE_DECK_SIZE;
|
||||||
|
const ChanceCard* card = &CHANCE_DECK[card_idx];
|
||||||
|
|
||||||
|
printf("\n✨ %s\n", card->description);
|
||||||
|
|
||||||
|
switch (card->type) {
|
||||||
|
case CHANCE_ADVANCE: {
|
||||||
|
int target = card->value;
|
||||||
|
|
||||||
|
// Handle special "Nearest" logic
|
||||||
|
if (target == TARGET_NEAREST_UTILITY) {
|
||||||
|
while (MONOPOLY_BOARD[p->position].type != TILE_UTILITY) {
|
||||||
|
p->position = (p->position + 1) % BOARD_SIZE;
|
||||||
|
}
|
||||||
|
} else if (target == TARGET_NEAREST_RAILROAD) {
|
||||||
|
while (MONOPOLY_BOARD[p->position].type != TILE_RAILROAD) {
|
||||||
|
p->position = (p->position + 1) % BOARD_SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check for passing GO during standard advance
|
||||||
|
if (target < p->position) {
|
||||||
|
p->balance += 200;
|
||||||
|
printf("🎉 Passed GO! Collected $200.\n");
|
||||||
|
}
|
||||||
|
p->position = target;
|
||||||
|
}
|
||||||
|
printf("➜ Moved to: %s\n", MONOPOLY_BOARD[p->position].name);
|
||||||
|
process_landing(p); // Re-evaluate logic for new square
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CHANCE_EARN:
|
||||||
|
p->balance += card->value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_SPEND:
|
||||||
|
p->balance -= card->value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_BACK:
|
||||||
|
p->position = (p->position - card->value + BOARD_SIZE) % BOARD_SIZE;
|
||||||
|
printf("⬅️ Moved back to: %s\n", MONOPOLY_BOARD[p->position].name);
|
||||||
|
process_landing(p);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_JAIL:
|
||||||
|
p->position = 10; // Index of Jail
|
||||||
|
p->jail_turns = 0;
|
||||||
|
p->is_in_jail = true;
|
||||||
|
just_sent_to_jail = true;
|
||||||
|
printf("🚨 Sent directly to JAIL!\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_SPEND_EACH_PLAYER:
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
|
if (players[i].id != p->id && !players[i].is_bankrupt) {
|
||||||
|
p->balance -= card->value;
|
||||||
|
players[i].balance += card->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_JAIL_FREE:
|
||||||
|
p->jail_free_cards++;
|
||||||
|
printf("🔑 You received a 'Get Out of Jail Free' card! (Total: %d)\n", p->jail_free_cards);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CHANCE_REPAIRS:
|
||||||
|
// TODO: Logic placeholder: calculate based on houses/hotels owned
|
||||||
|
printf("🔧 Repairs calculated based on your property development.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_community_chest(Player* p) {
|
||||||
|
int card_idx = rand() % COMMUNITY_DECK_SIZE;
|
||||||
|
const CommunityCard* card = &COMMUNITY_DECK[card_idx];
|
||||||
|
|
||||||
|
printf("\n📦 %s\n", card->description);
|
||||||
|
|
||||||
|
switch (card->type) {
|
||||||
|
case COMMUNITY_ADVANCE:
|
||||||
|
p->position = card->value;
|
||||||
|
p->balance += 200; // Always Go in this set
|
||||||
|
printf("➜ Moved to Go! Balance: $%d\n", p->balance);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_EARN:
|
||||||
|
p->balance += card->value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_SPEND:
|
||||||
|
p->balance -= card->value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_EARN_EACH_PLAYER:
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i++) {
|
||||||
|
if (players[i].id != p->id && !players[i].is_bankrupt) {
|
||||||
|
players[i].balance -= card->value;
|
||||||
|
p->balance += card->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_JAIL:
|
||||||
|
p->position = 10;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
p->is_in_jail = true;
|
||||||
|
just_sent_to_jail = true;
|
||||||
|
printf("🚨 Go to Jail! (Do not pass Go, do not collect $200)\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_REPAIRS:
|
||||||
|
// logic for $40/house and $115/hotel
|
||||||
|
printf("🔧 Repairs assessed.\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COMMUNITY_JAIL_FREE:
|
||||||
|
p->jail_free_cards++;
|
||||||
|
printf("🔑 Card stored. (Total: %d)\n", p->jail_free_cards);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
76
games/monopoly/monopoly_board.h
Normal file
76
games/monopoly/monopoly_board.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#ifndef MONOPOLY_BOARD_H
|
||||||
|
#include <stddef.h>
|
||||||
|
#define MONOPOLY_BOARD_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
TILE_GO,
|
||||||
|
TILE_PROPERTY,
|
||||||
|
TILE_COMMUNITY_CHEST,
|
||||||
|
TILE_TAX,
|
||||||
|
TILE_RAILROAD,
|
||||||
|
TILE_CHANCE,
|
||||||
|
TILE_JAIL,
|
||||||
|
TILE_UTILITY,
|
||||||
|
TILE_FREE_PARKING,
|
||||||
|
TILE_GO_TO_JAIL
|
||||||
|
} TileType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* name;
|
||||||
|
TileType type;
|
||||||
|
bool is_corner;
|
||||||
|
int cost; // 0 if not applicable
|
||||||
|
const char* color; // Hex string, NULL if not property
|
||||||
|
int rent[6]; // Base, 1H, 2H, 3H, 4H, Hotel
|
||||||
|
int group[3]; // Group ID, Position in group, Total in group
|
||||||
|
int house_cost; // Cost to build
|
||||||
|
} BoardTile;
|
||||||
|
|
||||||
|
#define BOARD_SIZE 40
|
||||||
|
|
||||||
|
static const BoardTile MONOPOLY_BOARD[BOARD_SIZE] = {
|
||||||
|
{"Go", TILE_GO, true, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Mediterranean Avenue", TILE_PROPERTY, false, 60, "#955438", {2, 10, 30, 90, 160, 250}, {1, 1, 2}, 50},
|
||||||
|
{"Community Chest", TILE_COMMUNITY_CHEST, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Baltic Avenue", TILE_PROPERTY, false, 60, "#955438", {4, 20, 60, 180, 320, 450}, {1, 2, 2}, 50},
|
||||||
|
{"Income Tax", TILE_TAX, false, 200, NULL, {0}, {0}, 0},
|
||||||
|
{"Reading Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 1, 4}, 0},
|
||||||
|
{"Oriental Avenue", TILE_PROPERTY, false, 100, "#aae0fa", {6, 30, 90, 270, 400, 550}, {2, 1, 3}, 50},
|
||||||
|
{"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Vermont Avenue", TILE_PROPERTY, false, 100, "#aae0fa", {6, 30, 90, 270, 400, 550}, {2, 2, 3}, 50},
|
||||||
|
{"Connecticut Avenue", TILE_PROPERTY, false, 120, "#aae0fa", {8, 40, 100, 300, 450, 600}, {2, 3, 3}, 50},
|
||||||
|
{"Jail", TILE_JAIL, true, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"St. Charles Place", TILE_PROPERTY, false, 140, "#d93a96", {10, 50, 150, 450, 625, 750}, {3, 1, 3}, 100},
|
||||||
|
{"Electric Company", TILE_UTILITY, false, 150, NULL, {0}, {10, 1, 2}, 0},
|
||||||
|
{"States Avenue", TILE_PROPERTY, false, 140, "#d93a96", {10, 50, 150, 450, 625, 750}, {3, 2, 3}, 100},
|
||||||
|
{"Virginia Avenue", TILE_PROPERTY, false, 160, "#d93a96", {12, 60, 180, 500, 700, 900}, {3, 3, 3}, 100},
|
||||||
|
{"Pennsylvania Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 2, 4}, 0},
|
||||||
|
{"St. James Place", TILE_PROPERTY, false, 180, "#f7941d", {14, 70, 200, 550, 750, 950}, {4, 1, 3}, 100},
|
||||||
|
{"Community Chest", TILE_COMMUNITY_CHEST, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Tennessee Avenue", TILE_PROPERTY, false, 180, "#f7941d", {14, 70, 200, 550, 750, 950}, {4, 2, 3}, 100},
|
||||||
|
{"New York Avenue", TILE_PROPERTY, false, 200, "#f7941d", {16, 80, 220, 600, 800, 1000}, {4, 3, 3}, 100},
|
||||||
|
{"Free Parking", TILE_FREE_PARKING, true, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Kentucky Avenue", TILE_PROPERTY, false, 220, "#ed1b24", {18, 90, 250, 700, 875, 1050}, {5, 1, 3}, 150},
|
||||||
|
{"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Indiana Avenue", TILE_PROPERTY, false, 220, "#ed1b24", {18, 90, 250, 700, 875, 1050}, {5, 2, 3}, 150},
|
||||||
|
{"Illnois Avenue", TILE_PROPERTY, false, 240, "#ed1b24", {20, 100, 300, 750, 925, 1100}, {5, 3, 3}, 150},
|
||||||
|
{"B. & O. Railroad", TILE_RAILROAD, false, 200, NULL, {0}, {9, 3, 4}, 0},
|
||||||
|
{"Atlatic Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 1, 3}, 150},
|
||||||
|
{"Ventura Avenue", TILE_PROPERTY, false, 260, "#fef200", {22, 110, 330, 800, 975, 1150}, {6, 2, 3}, 150},
|
||||||
|
{"Water Works", TILE_UTILITY, false, 150, NULL, {0}, {10, 2, 2}, 0},
|
||||||
|
{"Marvin Gardens", TILE_PROPERTY, false, 280, "#fef200", {24, 120, 360, 850, 1025, 1200}, {6, 3, 3}, 150},
|
||||||
|
{"Go To Jail", TILE_GO_TO_JAIL, true, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Pacific Avenue", TILE_PROPERTY, false, 300, "#1fb25a", {26, 130, 390, 900, 1100, 1275}, {7, 1, 3}, 200},
|
||||||
|
{"North Carolina Avenue", TILE_PROPERTY, false, 300, "#1fb25a", {26, 130, 390, 900, 1100, 1275}, {7, 2, 3}, 200},
|
||||||
|
{"Community Chest", TILE_COMMUNITY_CHEST, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Pennsylvania Avenue", TILE_PROPERTY, false, 320, "#1fb25a", {28, 150, 450, 1000, 1200, 1400}, {7, 3, 3}, 200},
|
||||||
|
{"Shortline", TILE_RAILROAD, false, 200, NULL, {0}, {9, 4, 4}, 0},
|
||||||
|
{"Chance", TILE_CHANCE, false, 0, NULL, {0}, {0}, 0},
|
||||||
|
{"Park Place", TILE_PROPERTY, false, 350, "#0072bb", {35, 175, 500, 1100, 1300, 1500}, {8, 1, 2}, 200},
|
||||||
|
{"Luxury Tax", TILE_TAX, false, 100, NULL, {0}, {0}, 0},
|
||||||
|
{"Boardwalk", TILE_PROPERTY, false, 400, "#0072bb", {50, 200, 600, 1400, 1700, 2000}, {8, 2, 2}, 200}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
218
games/monopoly/monopoly_game.cpp
Normal file
218
games/monopoly/monopoly_game.cpp
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// MONOPOLY GAME IMPLEMENTATION (for custom console)
|
||||||
|
// ============================================================================
|
||||||
|
// Refactored from console version to use Game interface and rendering/input system
|
||||||
|
|
||||||
|
#include "monopoly_game.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include "player.h"
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
// --- Constructor ---
|
||||||
|
MonopolyGame::MonopolyGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
||||||
|
: Game(width, height, renderer, gui, input_manager) {
|
||||||
|
players_count = 2;
|
||||||
|
current_player_idx = 0;
|
||||||
|
has_rolled = false;
|
||||||
|
double_rolls = 0;
|
||||||
|
just_sent_to_jail = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Initialize game state ---
|
||||||
|
void MonopolyGame::init() {
|
||||||
|
// Hardcoded 2 players for minimal version
|
||||||
|
init_player(&players[0], 0, "Elias", "Top Hat");
|
||||||
|
init_player(&players[1], 1, "Adolfo", "Racecar");
|
||||||
|
players_count = 2;
|
||||||
|
current_player_idx = 0;
|
||||||
|
has_rolled = false;
|
||||||
|
double_rolls = 0;
|
||||||
|
just_sent_to_jail = false;
|
||||||
|
selected_action = 0;
|
||||||
|
// TODO: Reset all board state, property ownership, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Handle input events (minimal: roll, buy, end turn) ---
|
||||||
|
bool MonopolyGame::update(const InputEvent& event) {
|
||||||
|
Player* p = &players[current_player_idx];
|
||||||
|
bool needs_redraw = false;
|
||||||
|
|
||||||
|
// If modal is open, any button closes it
|
||||||
|
if (show_property_modal) {
|
||||||
|
if (event.type == INPUT_BUTTON_0 || event.type == INPUT_BUTTON_1) {
|
||||||
|
show_property_modal = false;
|
||||||
|
modal_property_index = -1;
|
||||||
|
needs_redraw = true;
|
||||||
|
}
|
||||||
|
return needs_redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (event.type) {
|
||||||
|
case INPUT_BUTTON_0: // Cycle options
|
||||||
|
selected_action = (selected_action + 1) % ACTION_COUNT;
|
||||||
|
needs_redraw = true;
|
||||||
|
break;
|
||||||
|
case INPUT_BUTTON_1: // Select option
|
||||||
|
switch (selected_action) {
|
||||||
|
case 0: // Roll
|
||||||
|
if (!has_rolled && !p->is_in_jail) {
|
||||||
|
int dice1 = (rand() % 6) + 1;
|
||||||
|
int dice2 = (rand() % 6) + 1;
|
||||||
|
int total = dice1 + dice2;
|
||||||
|
int old_pos = p->position;
|
||||||
|
p->position = (p->position + total) % BOARD_SIZE;
|
||||||
|
if (p->position < old_pos) p->balance += 200;
|
||||||
|
has_rolled = true;
|
||||||
|
needs_redraw = true;
|
||||||
|
// Show property modal if landed on property/railroad/utility
|
||||||
|
const BoardTile* landed = &MONOPOLY_BOARD[p->position];
|
||||||
|
if (landed->type == TILE_PROPERTY || landed->type == TILE_RAILROAD || landed->type == TILE_UTILITY) {
|
||||||
|
show_property_modal = true;
|
||||||
|
modal_property_index = p->position;
|
||||||
|
}
|
||||||
|
// TODO: Handle doubles, jail, landing effects
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1: // Buy
|
||||||
|
if (has_rolled) {
|
||||||
|
const BoardTile* tile = &MONOPOLY_BOARD[p->position];
|
||||||
|
if ((tile->type == TILE_PROPERTY || tile->type == TILE_RAILROAD || tile->type == TILE_UTILITY) && p->balance >= tile->cost) {
|
||||||
|
bool owned = false;
|
||||||
|
for (int i = 0; i < p->property_count; ++i) {
|
||||||
|
if (p->properties_owned[i] == p->position) owned = true;
|
||||||
|
}
|
||||||
|
if (!owned) {
|
||||||
|
p->balance -= tile->cost;
|
||||||
|
p->properties_owned[p->property_count++] = p->position;
|
||||||
|
needs_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: Check for ownership by other players
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2: // End Turn
|
||||||
|
if (has_rolled) {
|
||||||
|
current_player_idx = (current_player_idx + 1) % players_count;
|
||||||
|
has_rolled = false;
|
||||||
|
double_rolls = 0;
|
||||||
|
just_sent_to_jail = false;
|
||||||
|
needs_redraw = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return needs_redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Draw game state (minimal: player info, current tile, actions) ---
|
||||||
|
void MonopolyGame::draw() {
|
||||||
|
Player* p = &players[current_player_idx];
|
||||||
|
const BoardTile* tile = &MONOPOLY_BOARD[p->position];
|
||||||
|
|
||||||
|
// Title
|
||||||
|
renderer->draw_string_scaled(10, 10, "Monopoly (Minimal)", 2);
|
||||||
|
|
||||||
|
// --- Player Stats (Right Side) ---
|
||||||
|
int stats_x = width - 180;
|
||||||
|
int y = 20;
|
||||||
|
char buf[128];
|
||||||
|
// Name (Big)
|
||||||
|
renderer->draw_string_scaled(stats_x, y, p->name, 2);
|
||||||
|
y += 40;
|
||||||
|
// Money
|
||||||
|
snprintf(buf, sizeof(buf), "$%d", p->balance);
|
||||||
|
renderer->draw_string_scaled(stats_x, y, buf, 2);
|
||||||
|
y += 30;
|
||||||
|
// Properties
|
||||||
|
int prop_count = 0;
|
||||||
|
for (int i = 0; i < p->property_count; ++i) {
|
||||||
|
int prop_idx = p->properties_owned[i];
|
||||||
|
if (prop_idx >= 0 && MONOPOLY_BOARD[prop_idx].type == TILE_PROPERTY) prop_count++;
|
||||||
|
}
|
||||||
|
snprintf(buf, sizeof(buf), "Properties: %d", prop_count);
|
||||||
|
renderer->draw_string_scaled(stats_x, y, buf, 2);
|
||||||
|
y += 30;
|
||||||
|
// Monopoly count
|
||||||
|
int monopoly_count = 0;
|
||||||
|
// For each group, check if player owns all properties in group
|
||||||
|
for (int group = 1; group <= 8; ++group) {
|
||||||
|
int group_total = 0, group_owned = 0;
|
||||||
|
for (int i = 0; i < BOARD_SIZE; ++i) {
|
||||||
|
if (MONOPOLY_BOARD[i].type == TILE_PROPERTY && MONOPOLY_BOARD[i].group[0] == group) {
|
||||||
|
group_total++;
|
||||||
|
for (int j = 0; j < p->property_count; ++j) {
|
||||||
|
if (p->properties_owned[j] == i) group_owned++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (group_total > 0 && group_total == group_owned) monopoly_count++;
|
||||||
|
}
|
||||||
|
snprintf(buf, sizeof(buf), "Monopolies: %d", monopoly_count);
|
||||||
|
renderer->draw_string_scaled(stats_x, y, buf, 2);
|
||||||
|
|
||||||
|
// --- Main Info (Left Side) ---
|
||||||
|
snprintf(buf, sizeof(buf), "Player: %s ($%d)", p->name, p->balance);
|
||||||
|
renderer->draw_string_scaled(10, 30, buf, 2);
|
||||||
|
|
||||||
|
snprintf(buf, sizeof(buf), "Location: %s", tile->name);
|
||||||
|
renderer->draw_string_scaled(10, 50, buf, 2);
|
||||||
|
|
||||||
|
// Show property modal window if needed
|
||||||
|
if (show_property_modal && modal_property_index >= 0) {
|
||||||
|
const BoardTile* mprop = &MONOPOLY_BOARD[modal_property_index];
|
||||||
|
int win_w = 320, win_h = 180;
|
||||||
|
int win_x = (width - win_w) / 2, win_y = (height - win_h) / 2;
|
||||||
|
LowLevelWindow* win = gui->draw_new_window(win_x, win_y, win_w, win_h, "Property Info");
|
||||||
|
// gui->current_font = renderer->get_current_font();
|
||||||
|
int py = win_y + 30;
|
||||||
|
char pbuf[128];
|
||||||
|
if (mprop->type == TILE_PROPERTY) {
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "%s", mprop->name);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
py += 30;
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "Color: %s", mprop->color ? mprop->color : "-");
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
py += 25;
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "Cost: $%d", mprop->cost);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
py += 25;
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "Rent: $%d", mprop->rent[0]);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
py += 25;
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "House Cost: $%d", mprop->house_cost);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
} else if (mprop->type == TILE_RAILROAD || mprop->type == TILE_UTILITY) {
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "%s", mprop->name);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
py += 30;
|
||||||
|
snprintf(pbuf, sizeof(pbuf), "Cost: $%d", mprop->cost);
|
||||||
|
renderer->draw_string_scaled(win_x + 20, py, pbuf, 2);
|
||||||
|
}
|
||||||
|
renderer->draw_string_scaled(win_x + 20, win_y + win_h - 40, "Press any button...", 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw action menu (highlight selected)
|
||||||
|
const char* actions[ACTION_COUNT] = {"Roll Dice", "Buy Property", "End Turn"};
|
||||||
|
for (int i = 0; i < ACTION_COUNT; ++i) {
|
||||||
|
int y = height - 80 + i * 20;
|
||||||
|
snprintf(buf, sizeof(buf), "%s%s", (i == selected_action) ? "> " : " ", actions[i]);
|
||||||
|
renderer->draw_string_scaled(10, y, buf, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// renderer->draw_string_scaled(10, height - 20, "BTN0: Next Option BTN1: Select", 2);
|
||||||
|
|
||||||
|
// TODO: Draw board, all players, property ownership, jail, chance, etc.
|
||||||
|
// TODO: Add win/lose/game over conditions
|
||||||
|
// TODO: Add touch support, more UI, etc.
|
||||||
|
}
|
||||||
47
games/monopoly/monopoly_game.h
Normal file
47
games/monopoly/monopoly_game.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// ============================================================================
|
||||||
|
// MONOPOLY GAME HEADER
|
||||||
|
// ============================================================================
|
||||||
|
// Concrete implementation of the Game interface for Monopoly
|
||||||
|
|
||||||
|
#ifndef MONOPOLY_GAME_H
|
||||||
|
#define MONOPOLY_GAME_H
|
||||||
|
|
||||||
|
#include "../../lib/game.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "monopoly_board.h"
|
||||||
|
#include "chance.h"
|
||||||
|
#include "community_chest.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Monopoly game implementation for the custom console
|
||||||
|
*
|
||||||
|
* - Button/touch input for actions
|
||||||
|
* - Board and player state
|
||||||
|
* - Rendering and input handling
|
||||||
|
*/
|
||||||
|
class MonopolyGame : public Game {
|
||||||
|
public:
|
||||||
|
MonopolyGame(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
|
||||||
|
void init() override;
|
||||||
|
bool update(const InputEvent& event) override;
|
||||||
|
void draw() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Game state and helpers
|
||||||
|
Player players[MAX_PLAYERS];
|
||||||
|
int players_count;
|
||||||
|
int current_player_idx;
|
||||||
|
bool has_rolled;
|
||||||
|
int double_rolls;
|
||||||
|
bool just_sent_to_jail;
|
||||||
|
|
||||||
|
// UI selection state
|
||||||
|
int selected_action; // 0: Roll, 1: Buy, 2: End Turn
|
||||||
|
static constexpr int ACTION_COUNT = 3;
|
||||||
|
|
||||||
|
// Modal property window state
|
||||||
|
bool show_property_modal = false;
|
||||||
|
int modal_property_index = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MONOPOLY_GAME_H
|
||||||
18
games/monopoly/player.c
Normal file
18
games/monopoly/player.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Implementation of init_player for Monopoly
|
||||||
|
#include "player.h"
|
||||||
|
|
||||||
|
void init_player(Player* p, int id, const char* name, const char* token) {
|
||||||
|
p->id = id;
|
||||||
|
p->name = name;
|
||||||
|
p->token = token;
|
||||||
|
p->balance = 1500; // Standard starting cash
|
||||||
|
p->position = 0; // Start at "Go"
|
||||||
|
p->is_in_jail = false;
|
||||||
|
p->jail_turns = 0;
|
||||||
|
p->jail_free_cards = 0;
|
||||||
|
p->is_bankrupt = false;
|
||||||
|
p->property_count = 0;
|
||||||
|
for(int i = 0; i < MAX_PROPERTIES; i++) {
|
||||||
|
p->properties_owned[i] = -1; // -1 indicates an empty slot
|
||||||
|
}
|
||||||
|
}
|
||||||
41
games/monopoly/player.h
Normal file
41
games/monopoly/player.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef PLAYER_H
|
||||||
|
#define PLAYER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "monopoly_board.h"
|
||||||
|
|
||||||
|
#define MAX_PLAYERS 4
|
||||||
|
#define MAX_PROPERTIES 40
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int id; // Player number (0-3)
|
||||||
|
const char* name; // Player display name
|
||||||
|
int balance; // Current cash
|
||||||
|
int position; // Current index on the MONOPOLY_BOARD (0-39)
|
||||||
|
|
||||||
|
// Status flags
|
||||||
|
bool is_in_jail;
|
||||||
|
int jail_turns; // How many turns they've spent in jail
|
||||||
|
int jail_free_cards; // Get Out of Jail Free cards
|
||||||
|
bool is_bankrupt;
|
||||||
|
|
||||||
|
// Inventory
|
||||||
|
int properties_owned[MAX_PROPERTIES]; // Indices of owned tiles
|
||||||
|
int property_count;
|
||||||
|
|
||||||
|
// Piece representation
|
||||||
|
const char* token; // e.g., "Top Hat", "Iron"
|
||||||
|
} Player;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a player with starting values
|
||||||
|
*/
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
void init_player(Player* p, int id, const char* name, const char* token);
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user