+#include "freertos/projdefs.h"
#include "belot.hpp"
#include "belot_common.hpp"
#include "deck.hpp"
extern SemaphoreHandle_t uart_mutex;
-Belot::Belot() : deck(), game_mode{NoTrumps}, bidding_over{false}, joined_players{0}, player_in_turn{0}, bid_winner{0}, multiplier{1}
+Belot::Belot() : deck(), game_mode{AllPass}, joined_players{0}, player_in_turn{0}, bid_winner{-1}, dealer{1}, multiplier{1}, pass_counter{0}, tricks_played{0}
{
for (uint8_t i = 0; i < NUM_TEAMS; i++)
{
// Not sure what to do with these here. ¯\_(ツ)_/¯
//player_card_inputs[i] = ;
//player_turn_semaphors[i] = ;
- pot[i] = Card();
+ trick[i] = Card();
+ previous_trick[i] = Card();
+ bids[i] = BidNone;
}
+
+ deck.shuffle();
}
int8_t Belot::join(const char *name, QueueHandle_t card_input, SemaphoreHandle_t turn_semaphor)
{
big_oof();
}
- xSemaphoreGive(player_turn_semaphors[player_in_turn]);
+ if (xSemaphoreGive(player_turn_semaphors[player_in_turn]) != pdTRUE)
+ {
+ big_oof();
+ }
}
return player_id;
-}
\ No newline at end of file
+}
+
+void Belot::signal_next_player()
+{
+ player_in_turn = (player_in_turn + 1) % NUM_PLAYERS;
+ if (xSemaphoreGive(player_turn_semaphors[player_in_turn]) != pdTRUE)
+ {
+ big_oof();
+ }
+}
+
+int8_t Belot::get_dealer_id()
+{
+ return dealer;
+}
+
+void Belot::split_deck()
+{
+ deck.split();
+}
+
+int Belot::draw_cards(Card *cards_buf, size_t num_cards)
+{
+ return deck.draw_multiple(cards_buf, num_cards);
+}
+
+int Belot::give_cards_to_player(Card *cards_buf, size_t num_cards, int8_t player_id)
+{
+ if (cards_buf == NULL)
+ {
+ return -1;
+ }
+ if (player_id < 0 || player_id >= NUM_PLAYERS)
+ {
+ return -2;
+ }
+
+ for (size_t i = 0; i < num_cards; i++)
+ {
+ if (xQueueSendToBack(player_card_inputs[player_id], &cards_buf[i], portMAX_DELAY) != pdTRUE)
+ {
+ return -3;
+ }
+ }
+
+ return 0;
+}
+
+const bid_t *Belot::view_bids()
+{
+ return (const bid_t *) bids;
+}
+
+int Belot::bid(bid_t bid, int8_t player_id)
+{
+ bid_t highest_bid = current_highest_bid();
+
+ if (bid == BidNone)
+ {
+ return -1;
+ }
+ if (bid == BidPass)
+ {
+ bids[player_id] = bid;
+ pass_counter++;
+ if (bidding_over())
+ {
+ // When the curent player ends his turn, the player_in_turn counter will be incremented and the dealer will deal the last cards
+ player_in_turn = (dealer + NUM_PLAYERS - 1) % NUM_PLAYERS;
+ }
+ return 0;
+ }
+ if (bid <= highest_bid)
+ {
+ return -2;
+ }
+ if (bid == BidContra)
+ {
+ multiplier = 2;
+ }
+ else if (bid == BidRecontra)
+ {
+ multiplier = 4;
+ }
+ else
+ {
+ switch (bid)
+ {
+ case BidNoTrumps: game_mode = NoTrumps; break;
+ case BidClubs: game_mode = ClubsTrump; break;
+ case BidDiamonds: game_mode = DiamondsTrump; break;
+ case BidHearts: game_mode = HeartsTrump; break;
+ case BidSpades: game_mode = SpadesTrump; break;
+ case BidAllTrumps: game_mode = AllTrumps; break;
+ default: break;
+ }
+ bid_winner = player_id;
+ }
+ bids[player_id] = bid;
+ pass_counter = 0;
+
+ return 0;
+}
+
+bid_t Belot::current_highest_bid()
+{
+ bid_t highest_bid = BidNone, next_bid;
+
+ for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+ {
+ next_bid = bids[(dealer + 1 + i) % NUM_PLAYERS];
+ if (next_bid > highest_bid)
+ {
+ highest_bid = next_bid;
+ }
+ }
+
+ return highest_bid;
+}
+
+bool Belot::bidding_over()
+{
+ bid_t highest_bid = current_highest_bid();
+
+ switch (highest_bid)
+ {
+ case BidNone: return false;
+ case BidPass: return pass_counter == NUM_PLAYERS;
+ default: return pass_counter == NUM_PLAYERS - 1;
+ }
+}
+
+game_mode_t Belot::get_game_mode()
+{
+ return game_mode;
+}
+
+int Belot::return_card(Card *card)
+{
+ if (card == NULL)
+ {
+ return -1;
+ }
+
+ deck.add(card);
+ *card = NO_CARD;
+
+ return 0;
+}
+
+const Card *Belot::view_trick()
+{
+ return (const Card*) trick;
+}
+
+const Card *Belot::view_previous_trick()
+{
+ return (const Card*) previous_trick;
+}
+
+int Belot::play_card(Card *card, int8_t player_id)
+{
+ if (card == NULL)
+ {
+ return -1;
+ }
+ if (player_id < 0 || player_id >= NUM_PLAYERS)
+ {
+ return -2;
+ }
+
+ trick[player_id] = *card;
+ return_card(card);
+
+ if (trick[(player_id + 1) % NUM_PLAYERS] != NO_CARD)
+ {
+ int8_t winning_team = calculate_score_round((player_id + 1) % NUM_PLAYERS);
+
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print("\nScore:\nEven team - ");
+ Serial.println(score_round[0]);
+ Serial.print("Odd team - ");
+ Serial.println(score_round[1]);
+ Serial.println();
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+
+ tricks_played++;
+ if (tricks_played == TRICKS_PER_GAME)
+ {
+ score_round[winning_team] += 10;
+ tricks_played = 0;
+ calculate_score_game();
+ }
+ }
+
+ return 0;
+}
+
+int8_t Belot::calculate_score_round(uint8_t first_card)
+{
+ card_suite_t requested_suite = trick[first_card].get_suite();
+ uint8_t highest_strenght = trick[first_card].get_strength(game_mode);
+ uint8_t winning_player_id = first_card;
+
+ for (uint8_t i = 1; i < NUM_PLAYERS; i++)
+ {
+ Card next_card = trick[(first_card + i) % NUM_PLAYERS];
+
+ if (next_card.get_suite() == requested_suite && next_card.get_strength(game_mode) > highest_strenght)
+ {
+ highest_strenght = next_card.get_strength(game_mode);
+ winning_player_id = (first_card + i) % NUM_PLAYERS;
+ }
+ }
+
+ uint8_t winning_team = winning_player_id % 2;
+
+ for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+ {
+ score_round[winning_team] += trick[i].get_points(game_mode);
+ previous_trick[i] = trick[i];
+ trick[i] = NO_CARD;
+ }
+ player_in_turn = (winning_player_id + NUM_PLAYERS - 1) % NUM_PLAYERS;
+
+ return winning_team;
+}
+
+void Belot::calculate_score_game()
+{
+ // TODO
+ while (true)
+ {
+ vTaskDelay(1000);
+ }
+}
#define NUM_TEAMS 2
#define NUM_PLAYERS 4
#define CARDS_PER_PLAYER 8
+#define TRICKS_PER_GAME 8
+
+typedef enum
+{
+ BidNone = 0,
+ BidPass = 1,
+ BidNoTrumps = 2,
+ BidClubs = 3,
+ BidDiamonds = 4,
+ BidHearts = 5,
+ BidSpades = 6,
+ BidAllTrumps = 7,
+ BidContra = 8,
+ BidRecontra = 9
+}
+bid_t;
class Belot
{
private:
Deck deck;
game_mode_t game_mode;
- bool bidding_over;
uint8_t joined_players;
uint8_t player_in_turn;
- uint8_t bid_winner;
+ int8_t bid_winner;
+ int8_t dealer;
uint8_t multiplier;
+ uint8_t pass_counter;
+ uint8_t tricks_played;
uint16_t score_game[NUM_TEAMS];
uint16_t score_round[NUM_TEAMS];
const char *player_names[NUM_PLAYERS];
QueueHandle_t player_card_inputs[NUM_PLAYERS];
SemaphoreHandle_t player_turn_semaphors[NUM_PLAYERS];
- Card pot[NUM_PLAYERS];
+ Card trick[NUM_PLAYERS];
+ Card previous_trick[NUM_PLAYERS];
+ bid_t bids[NUM_PLAYERS];
+
+ bid_t current_highest_bid();
+ int8_t calculate_score_round(uint8_t first_card);
+ void calculate_score_game();
public:
Belot();
int8_t join(const char *name, QueueHandle_t card_input, SemaphoreHandle_t turn_semaphor);
+ void signal_next_player();
+ int8_t get_dealer_id();
+ void split_deck();
+ int draw_cards(Card *cards_buf, size_t num_cards);
+ int give_cards_to_player(Card *cards_buf, size_t num_cards, int8_t player_id);
+ const bid_t *view_bids();
+ int bid(bid_t bid, int8_t player_id);
+ bool bidding_over();
+ game_mode_t get_game_mode();
+ int return_card(Card *card);
+ const Card *view_trick();
+ const Card *view_previous_trick();
+ int play_card(Card *card, int8_t player_id);
};
#endif
\ No newline at end of file
typedef enum
{
+ AllPass,
NoTrumps,
ClubsTrump,
DiamondsTrump,
#include "card.hpp"
#include "belot_common.hpp"
+#include <string.h>
#include <stdint.h>
#include <cstddef>
-// 7 8 9 10 J Q K A
-static const uint8_t no_trump_strenghts[NUM_OF_VALUES] = {0, 1, 2, 6, 3, 4, 5, 7};
-static const uint8_t trump_strenghts[NUM_OF_VALUES] = {8, 9, 14, 12, 15, 10, 11, 13};
-static const uint8_t no_trump_points[NUM_OF_VALUES] = {0, 0, 0, 10, 2, 3, 4, 11};
-static const uint8_t trump_points[NUM_OF_VALUES] = {0, 0, 14, 10, 20, 3, 4, 11};
+const Card NO_CARD = Card(InvalidSuite, InvalidValue);
-Card::Card() : suite{Spades}, value{Ace}
+// 7 8 9 10 J Q K A INVALID
+static const int8_t no_trump_strenghts[NUM_OF_VALUES + 1] = {0, 1, 2, 6, 3, 4, 5, 7, -1};
+static const int8_t trump_strenghts[NUM_OF_VALUES + 1] = {8, 9, 14, 12, 15, 10, 11, 13, -1};
+static const int8_t no_trump_points[NUM_OF_VALUES + 1] = {0, 0, 0, 10, 2, 3, 4, 11, -1};
+static const int8_t trump_points[NUM_OF_VALUES + 1] = {0, 0, 14, 10, 20, 3, 4, 11, -1};
+
+Card::Card() : suite{InvalidSuite}, value{InvalidValue}
{}
Card::Card(card_suite_t suite_init, card_value_t value_init) : suite{suite_init}, value{value_init}
case HeartsTrump: return suite == Hearts ? trump_strenghts[(size_t) value] : no_trump_strenghts[(size_t) value];
case SpadesTrump: return suite == Spades ? trump_strenghts[(size_t) value] : no_trump_strenghts[(size_t) value];
case AllTrumps: return trump_strenghts[(size_t) value];
- default: return -1;
+ default: return -2;
}
}
case HeartsTrump: return suite == Hearts ? trump_points[(size_t) value] : no_trump_points[(size_t) value];
case SpadesTrump: return suite == Spades ? trump_points[(size_t) value] : no_trump_points[(size_t) value];
case AllTrumps: return trump_points[(size_t) value];
- default: return -1;
+ default: return -2;
+ }
+}
+
+// Requires at least 18 bytes of buffer
+int Card::print(char *buf)
+{
+ const char *value_text = NULL;
+ const char *suite_text = NULL;
+
+ if (buf == NULL)
+ {
+ return -1;
+ }
+
+ switch (value)
+ {
+ case Seven: value_text = "Seven"; break;
+ case Eight: value_text = "Eight"; break;
+ case Nine: value_text = "Nine"; break;
+ case Ten: value_text = "Ten"; break;
+ case Jack: value_text = "Jack"; break;
+ case Queen: value_text = "Queen"; break;
+ case King: value_text = "King"; break;
+ case Ace: value_text = "Ace"; break;
+ default: value_text = ""; break;
}
+ switch (suite)
+ {
+ case Clubs: suite_text = " of Clubs"; break;
+ case Diamonds: suite_text = " of Diamonds"; break;
+ case Hearts: suite_text = " of Hearts"; break;
+ case Spades: suite_text = " of Spades"; break;
+ default: suite_text = ""; break;
+ }
+ strcpy(buf, value_text);
+ strcpy(buf + strlen(value_text), suite_text);
+
+ return 0;
+}
+
+bool operator==(const Card lhs, const Card rhs)
+{
+ return (lhs.suite == rhs.suite && lhs.value == rhs.value);
}
+
+bool operator!=(const Card lhs, const Card rhs)
+{
+ return (lhs.suite != rhs.suite || lhs.value != rhs.value);
+}
+
Clubs = 0,
Diamonds = 1,
Hearts = 2,
- Spades = 3
+ Spades = 3,
+ InvalidSuite
}
card_suite_t;
Jack = 4,
Queen = 5,
King = 6,
- Ace = 7
+ Ace = 7,
+ InvalidValue
}
card_value_t;
card_value_t get_value();
int get_strength(game_mode_t gamemode);
int get_points(game_mode_t gamemode);
+ int print(char *buf);
+ friend bool operator==(const Card lhs, const Card rhs);
+ friend bool operator!=(const Card lhs, const Card rhs);
};
+extern const Card NO_CARD;
+
#endif
int Deck::draw(Card *card_ptr)
{
- if (remaining == 0)
+ if (card_ptr == NULL)
{
return -1;
}
+ if (remaining == 0)
+ {
+ return -2;
+ }
*card_ptr = cards[start];
start = (start + 1) % DECK_SIZE;
int Deck::draw_multiple(Card *cards_buf, size_t num_cards)
{
- if (remaining < num_cards)
+ if (cards_buf == NULL)
{
return -1;
}
+ if (remaining < num_cards)
+ {
+ return -2;
+ }
if (start + num_cards > DECK_SIZE)
{
int Deck::add(Card *card_ptr)
{
- if (remaining >= DECK_SIZE)
+ if (card_ptr == NULL)
{
return -1;
}
+ if (remaining >= DECK_SIZE)
+ {
+ return -2;
+ }
end = (end + 1) % DECK_SIZE;
cards[end] = *card_ptr;
int Deck::add_multiple(Card *cards_buf, size_t num_cards)
{
- if (remaining > DECK_SIZE - num_cards)
+ if (cards_buf == NULL)
{
return -1;
}
+ if (remaining > DECK_SIZE - num_cards)
+ {
+ return -2;
+ }
if (end + num_cards > DECK_SIZE)
{
void Deck::split()
{
Card buf[DECK_SIZE];
+ char buffer[600];
+ print(buffer);
+ Serial.println(buffer);
+ Serial.println("===============================================");
// random is NOT thread-safe but using it here is fine because only one thread modifies the deck at a time (the dealer)
size_t pos = (size_t) random() % remaining;
draw_multiple(buf, pos);
add_multiple(buf, pos);
+ print(buffer);
+ Serial.println(buffer);
+}
+
+// Requires at least 577 bytes of buffer
+int Deck::print(char *buf)
+{
+ char card_text[18];
+ size_t pos = 0;
+
+ for (uint8_t i = 0; i < DECK_SIZE; i++)
+ {
+ cards[i].print(card_text);
+ strcpy(buf + pos, card_text);
+ pos += strlen(card_text);
+ buf[pos] = '\n';
+ pos++;
+ }
+
+ return 0;
}
int add_multiple(Card *cards_buf, size_t num_cards);
void shuffle();
void split();
+ int print(char *buf);
};
#endif
#include "belot.hpp"
#include "belot_common.hpp"
#include "card.hpp"
+#include "player.hpp"
-#define THREAD_STACK_SIZE 1024
+#include <stdlib.h>
+
+#define THREAD_STACK_SIZE 2048
#define PLAYERS 6
+#define NOISE_PIN 34
-static void belot_player(__attribute__((unused)) void *args);
+static void play_belot(__attribute__((unused)) void *args);
SemaphoreHandle_t uart_mutex = xSemaphoreCreateMutex();
+
static Belot *lobby = NULL;
-static SemaphoreHandle_t lobby_mutex = xSemaphoreCreateMutex();
+static SemaphoreHandle_t lobby_mutex;
static const char *player_names[PLAYERS] = {"Asen", "Boris", "Violeta", "Georgi", "Dimitar", "Elena"};
static TaskHandle_t player_handles[PLAYERS];
void setup()
{
+ pinMode(NOISE_PIN, INPUT);
+ //srandom(analogRead(NOISE_PIN));
+ srandom(0);
+
+ lobby_mutex = xSemaphoreCreateMutex();
+ if (lobby_mutex == NULL)
+ {
+ big_oof();
+ }
+
Serial.begin(38400);
// Without some delay here Arduino IDE misses the first output
delay(1000);
// We can skip taking and giving uart_mutex here because no other threads are running yet
Serial.println("The lobby is open!");
+ delay(1000);
for (uint8_t i = 0; i < PLAYERS; i++)
{
- if (xTaskCreate(belot_player, player_names[i], THREAD_STACK_SIZE, NULL, 1, &player_handles[i]) != pdPASS)
+ if (xTaskCreate(play_belot, player_names[i], THREAD_STACK_SIZE, NULL, 1, &player_handles[i]) != pdPASS)
{
big_oof();
}
void loop()
{}
-void belot_player(__attribute__((unused)) void *args)
+void play_belot(__attribute__((unused)) void *args)
{
- const char *name = pcTaskGetName(NULL);
- QueueHandle_t card_input = xQueueCreate(CARDS_PER_PLAYER, sizeof(Card));
- SemaphoreHandle_t turn_semaphor = xSemaphoreCreateBinary();
-
- if (card_input == NULL || turn_semaphor == NULL)
- {
- big_oof();
- }
+ Player player = Player(pcTaskGetName(NULL));
+ // Games loop
while (true)
{
- Belot *game;
- int8_t player_id = -1;
- bool created_game = false;
+ player.join_or_start_game(&lobby, lobby_mutex);
- while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
- Serial.print(name);
- Serial.println(" entered the lobby.");
- if (xSemaphoreGive(uart_mutex) != pdTRUE)
- {
- big_oof();
- }
- while (player_id < 0)
+ int8_t id = player.get_id();
+ __attribute__((unused)) const Card *hand = player.view_hand();
+ __attribute__((unused)) const Card *trick = player.view_trick();
+ __attribute__((unused)) const Card *previous_trick = player.view_previous_trick();
+ __attribute__((unused)) const bid_t *bids = player.view_bids();
+
+ // Rounds loop
+ while (true)
{
- if (xSemaphoreTake(lobby_mutex, portMAX_DELAY) == pdTRUE)
+ int8_t dealer_id = player.get_dealer_id();
+
+ if (id == dealer_id)
{
- if (lobby == NULL)
- {
- // No existing game, create a new one
- try
- {
- game = new Belot;
- lobby = game;
- }
- catch (__attribute__((unused)) int err)
- {
- big_oof();
- }
- created_game = true;
- player_id = lobby->join(name, card_input, turn_semaphor);
- }
- else
- {
- player_id = lobby->join(name, card_input, turn_semaphor);
- }
- if (xSemaphoreGive(lobby_mutex) != pdTRUE)
- {
- big_oof();
- }
+ player.deal_first();
}
- }
-
- // Wait for enough players to join
- while (xSemaphoreTake(turn_semaphor, portMAX_DELAY) != pdTRUE);
- // If you created the game, free the lobby
- if (created_game)
- {
- while (xSemaphoreTake(lobby_mutex, portMAX_DELAY) != pdTRUE);
- lobby = NULL;
- if (xSemaphoreGive(lobby_mutex) != pdTRUE)
+ else if ((id + 1) % NUM_PLAYERS == dealer_id)
{
- big_oof();
+ player.split_deck();
}
+ player.collect_cards();
+ while (!player.bidding_over())
+ {
+ // 30 godini TUES, 30 godini vsichko koz!
+ player.get_game_mode() == AllTrumps ? player.bid(BidPass) : player.bid(BidAllTrumps);
+ }
+ if (player.get_game_mode() == AllPass)
+ {
+ player.end_round();
+ continue;
+ }
+ if (id == dealer_id)
+ {
+ player.deal_second();
+ }
+ player.collect_cards();
+ // Tricks loop
+ for (uint8_t i = 0; i < TRICKS_PER_GAME; i++)
+ {
+ vTaskDelay(1000);
+ // Just throw the cards in order, who cares
+ player.play_card(i);
+ }
+ player.end_round();
}
- // Starting a new game
- while (true)
- {
- vTaskDelay(100);
- }
-
- // The game has ended
- if (created_game)
- {
- delete game;
- }
+ player.end_game();
}
}
--- /dev/null
+#include "freertos/projdefs.h"
+#include "player.hpp"
+#include "belot.hpp"
+#include "belot_common.hpp"
+#include "card.hpp"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "freertos/queue.h"
+
+#include <stdint.h>
+
+extern SemaphoreHandle_t uart_mutex;
+
+Player::Player(const char *new_name) : player_id{-1}, game{NULL}, created_game{false}, cards_received{0}
+{
+ // Not a big deal if name is NULL, Serial.print does check for NULL pointers.
+ // Constructors can't return an error and I just can't be bothered with C++ exceptions for this.
+ name = new_name;
+ card_input = xQueueCreate(CARDS_PER_PLAYER, sizeof(Card));
+ turn_semaphor = xSemaphoreCreateBinary();
+
+ if (card_input == NULL || turn_semaphor == NULL)
+ {
+ big_oof();
+ }
+
+ for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+ {
+ hand[i] = Card();
+ }
+}
+
+Player::~Player()
+{
+ vQueueDelete(card_input);
+ vSemaphoreDelete(turn_semaphor);
+}
+
+void Player::wait_for_turn()
+{
+ while (xSemaphoreTake(turn_semaphor, portMAX_DELAY) != pdTRUE);
+}
+
+int Player::join_or_start_game(Belot **lobby, SemaphoreHandle_t lobby_mutex)
+{
+ if (lobby == NULL || lobby_mutex == NULL)
+ {
+ return -1;
+ }
+
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ Serial.println(" entered the lobby.");
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+
+ while (player_id < 0)
+ {
+ if (xSemaphoreTake(lobby_mutex, portMAX_DELAY) == pdTRUE)
+ {
+ if (*lobby == NULL)
+ {
+ // No existing game, create a new one
+ try
+ {
+ game = new Belot;
+ *lobby = game;
+ }
+ catch (__attribute__((unused)) int err)
+ {
+ big_oof();
+ }
+ created_game = true;
+ player_id = game->join(name, card_input, turn_semaphor);
+ }
+ else
+ {
+ game = *lobby;
+ player_id = game->join(name, card_input, turn_semaphor);
+ }
+ if (xSemaphoreGive(lobby_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+ }
+ }
+
+ // Wait for enough players to join
+ wait_for_turn();
+ // If you created the game, free the lobby
+ if (created_game)
+ {
+ while (xSemaphoreTake(lobby_mutex, portMAX_DELAY) != pdTRUE);
+ *lobby = NULL;
+ if (xSemaphoreGive(lobby_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+ }
+
+ return 0;
+}
+
+int Player::end_game()
+{
+ if (game == NULL)
+ {
+ return -1;
+ }
+
+ if (created_game)
+ {
+ delete game;
+ }
+ game = NULL;
+ created_game = false;
+ player_id = -1;
+
+ return 0;
+}
+
+int8_t Player::get_id()
+{
+ return player_id;
+}
+
+int8_t Player::get_dealer_id()
+{
+ return game->get_dealer_id();
+}
+
+void Player::split_deck()
+{
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ Serial.println(" splits the deck.");
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+ game->split_deck();
+ end_turn();
+}
+
+int Player::deal_two()
+{
+ Card buf[2];
+
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ Serial.println(" deals 2 cards to each player.");
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+
+ for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+ {
+ if (game->draw_cards(buf, 2))
+ {
+ return -1;
+ }
+ if (game->give_cards_to_player(buf, 2, (player_id + i) % NUM_PLAYERS))
+ {
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+int Player::deal_three()
+{
+ Card buf[3];
+
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ Serial.println(" deals 3 cards to each player.");
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+
+ for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+ {
+ if (game->draw_cards(buf, 3))
+ {
+ return -1;
+ }
+ if (game->give_cards_to_player(buf, 3, (player_id + i) % NUM_PLAYERS))
+ {
+ return -2;
+ }
+ }
+
+ return 0;
+}
+
+int Player::deal_first()
+{
+ int error_code = deal_three();
+ error_code += deal_two();
+ end_turn();
+ return error_code;
+}
+
+int Player::deal_second()
+{
+ int error_code = deal_three();
+ end_turn();
+ return error_code;
+}
+
+void Player::end_turn()
+{
+ game->signal_next_player();
+ wait_for_turn();
+}
+
+void Player::collect_cards()
+{
+ while (cards_received < CARDS_PER_PLAYER && xQueueReceive(card_input, &hand[cards_received], 0) == pdTRUE)
+ {
+ cards_received++;
+ }
+}
+
+const Card *Player::view_hand()
+{
+ return (const Card *) hand;
+}
+
+const bid_t *Player::view_bids()
+{
+ return game->view_bids();
+}
+
+int Player::bid(bid_t bid)
+{
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ switch (bid)
+ {
+ case BidPass: Serial.println(": pass"); break;
+ case BidNoTrumps: Serial.println(": no trumps"); break;
+ case BidClubs: Serial.println(": clubs"); break;
+ case BidDiamonds: Serial.println(": diamonds"); break;
+ case BidHearts: Serial.println(": hearts"); break;
+ case BidSpades: Serial.println(": spades"); break;
+ case BidAllTrumps: Serial.println(": all trumps"); break;
+ case BidContra: Serial.println(": contra"); break;
+ case BidRecontra: Serial.println(": re-contra"); break;
+ default: Serial.println(": "); break;
+ }
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+ int error_code = game->bid(bid, player_id);
+ if (!error_code)
+ {
+ end_turn();
+ }
+
+ return error_code;
+}
+
+bool Player::bidding_over()
+{
+ return game->bidding_over();
+}
+
+game_mode_t Player::get_game_mode()
+{
+ return game->get_game_mode();
+}
+
+void Player::end_round()
+{
+ for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+ {
+ if (hand[i] != NO_CARD)
+ {
+ game->return_card(&hand[i]);
+ }
+ }
+
+ end_turn();
+}
+
+const Card *Player::view_trick()
+{
+ return game->view_trick();
+}
+
+const Card *Player::view_previous_trick()
+{
+ return game->view_previous_trick();
+}
+
+int Player::play_card(uint8_t card_num)
+{
+ char card_text[20];
+
+ if (card_num >= CARDS_PER_PLAYER)
+ {
+ return -1;
+ }
+ if (hand[card_num] == NO_CARD)
+ {
+ return -2;
+ }
+
+ hand[card_num].print(card_text);
+ while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
+ Serial.print(name);
+ Serial.print(": ");
+ Serial.println(card_text);
+ if (xSemaphoreGive(uart_mutex) != pdTRUE)
+ {
+ big_oof();
+ }
+
+ int error_code = game->play_card(&hand[card_num], player_id);
+ if (!error_code)
+ {
+ end_turn();
+ }
+
+ return error_code;
+}
--- /dev/null
+#ifndef PLAYER_H
+#define PLAYER_H
+
+#include "belot.hpp"
+#include "card.hpp"
+
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "freertos/queue.h"
+
+#include <stdint.h>
+
+class Player
+{
+ private:
+ const char *name;
+ int8_t player_id;
+ Belot *game;
+ bool created_game;
+ uint8_t cards_received;
+ QueueHandle_t card_input;
+ SemaphoreHandle_t turn_semaphor;
+ Card hand[CARDS_PER_PLAYER];
+
+ void wait_for_turn();
+ int deal_two();
+ int deal_three();
+
+ public:
+ Player(const char *name);
+ ~Player();
+ int join_or_start_game(Belot **lobby, SemaphoreHandle_t lobby_mutex);
+ int end_game();
+ int8_t get_id();
+ int8_t get_dealer_id();
+ void split_deck();
+ int deal_first();
+ int deal_second();
+ void end_turn();
+ void collect_cards();
+ const Card *view_hand();
+ const bid_t *view_bids();
+ int bid(bid_t bid);
+ bool bidding_over();
+ game_mode_t get_game_mode();
+ void end_round();
+ const Card *view_trick();
+ const Card *view_previous_trick();
+ int play_card(uint8_t card_num);
+};
+
+#endif
\ No newline at end of file