]> kolegite.com Git - vmks.git/commitdiff
FEAT: first working version of the belot simulation
authorVladimir Garistov <vl.garistov@gmail.com>
Mon, 15 Apr 2024 04:29:58 +0000 (07:29 +0300)
committerVladimir Garistov <vl.garistov@gmail.com>
Mon, 15 Apr 2024 04:29:58 +0000 (07:29 +0300)
Examples/FreeRTOS/freertos_belot_sim/belot.cpp
Examples/FreeRTOS/freertos_belot_sim/belot.hpp
Examples/FreeRTOS/freertos_belot_sim/card.cpp
Examples/FreeRTOS/freertos_belot_sim/card.hpp
Examples/FreeRTOS/freertos_belot_sim/debug.hpp [new file with mode: 0644]
Examples/FreeRTOS/freertos_belot_sim/deck.cpp
Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.cpp [new file with mode: 0644]
Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.hpp [new file with mode: 0644]
Examples/FreeRTOS/freertos_belot_sim/freertos_belot_sim.ino
Examples/FreeRTOS/freertos_belot_sim/player.cpp

index 51ac4294bf6f9d7c49d047705037d873e8f7142c..42c268615e73491bfe658ce3dc2d37225bed5f68 100644 (file)
@@ -3,6 +3,7 @@
 #include "belot_common.hpp"
 #include "deck.hpp"
 #include "card.hpp"
+#include "debug.hpp"
 
 #include "freertos/FreeRTOS.h"
 #include "freertos/semphr.h"
@@ -11,8 +12,6 @@
 #include <stdint.h>
 #include <HardwareSerial.h>
 
-extern SemaphoreHandle_t uart_mutex;
-
 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++)
@@ -52,6 +51,7 @@ int8_t Belot::join(const char *name, QueueHandle_t card_input, SemaphoreHandle_t
 
   if (joined_players == NUM_PLAYERS)
   {
+    #ifdef DEBUG
     while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
     Serial.print(player_names[0]);
     Serial.print(" and ");
@@ -65,6 +65,7 @@ int8_t Belot::join(const char *name, QueueHandle_t card_input, SemaphoreHandle_t
     {
       big_oof();
     }
+    #endif
     if (xSemaphoreGive(player_turn_semaphors[player_in_turn]) != pdTRUE)
     {
       big_oof();
@@ -250,6 +251,7 @@ int Belot::play_card(Card *card, int8_t player_id)
   {
     int8_t winning_team = calculate_score_round((player_id + 1) % NUM_PLAYERS);
 
+    #ifdef DEBUG
     while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
     Serial.print("\nScore:\nEven team - ");
     Serial.println(score_round[0]);
@@ -260,6 +262,12 @@ int Belot::play_card(Card *card, int8_t player_id)
     {
       big_oof();
     }
+    #endif
+
+    for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+    {
+      trick[i] = NO_CARD;
+    }
 
     tricks_played++;
     if (tricks_played == TRICKS_PER_GAME)
@@ -275,21 +283,8 @@ int Belot::play_card(Card *card, int8_t player_id)
 
 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;
-    }
-  }
-
+  
+  int8_t winning_player_id = find_winning_card(trick, first_card, game_mode);
   uint8_t winning_team = winning_player_id % 2;
 
   for (uint8_t i = 0; i < NUM_PLAYERS; i++)
@@ -311,3 +306,40 @@ void Belot::calculate_score_game()
     vTaskDelay(1000);
   }
 }
+
+int8_t find_winning_card(const Card *cards, uint8_t first_card, game_mode_t game_mode)
+{
+  if (cards == NULL)
+  {
+    return -1;
+  }
+  if (game_mode == AllPass)
+  {
+    return -2;
+  }
+  
+  card_suite_t requested_suite = cards[first_card].get_suite();
+  uint8_t highest_strenght = cards[first_card].get_strength(game_mode);
+  uint8_t winning_card_num = first_card;
+
+  for (uint8_t i = 1; i < NUM_PLAYERS; i++)
+  {
+    Card next_card = cards[(first_card + i) % NUM_PLAYERS];
+
+    if (game_mode == NoTrumps || game_mode == AllTrumps)
+    {
+      if (next_card.get_suite() == requested_suite && next_card.get_strength(game_mode) > highest_strenght)
+      {
+        highest_strenght = next_card.get_strength(game_mode);
+        winning_card_num = (first_card + i) % NUM_PLAYERS;
+      }
+    }
+    else if (next_card.get_strength(game_mode) > highest_strenght)
+    {
+      highest_strenght = next_card.get_strength(game_mode);
+      winning_card_num = (first_card + i) % NUM_PLAYERS;
+    }
+  }
+
+  return winning_card_num;
+}
index f12a08c1530ddbbed447d7ab78809f5eaec4e840..a882d0b334e7df58e9c25d86f79ae489f8b5d9a6 100644 (file)
@@ -74,4 +74,6 @@ class Belot
     int play_card(Card *card, int8_t player_id);
 };
 
+int8_t find_winning_card(const Card *cards, uint8_t first_card, game_mode_t game_mode);
+
 #endif
\ No newline at end of file
index 8f17a2a572b2e3869ec37c9f54f38f67cead54f4..54462ffc124d7aeb1c011bab35da7c5e314104e4 100644 (file)
@@ -19,17 +19,17 @@ Card::Card() : suite{InvalidSuite}, value{InvalidValue}
 Card::Card(card_suite_t suite_init, card_value_t value_init) : suite{suite_init}, value{value_init}
 {}
 
-card_suite_t Card::get_suite()
+card_suite_t Card::get_suite() const
 {
   return suite;
 }
 
-card_value_t Card::get_value()
+card_value_t Card::get_value() const
 {
   return value;
 }
 
-int Card::get_strength(game_mode_t gamemode)
+int Card::get_strength(game_mode_t gamemode) const
 {
   switch (gamemode)
   {
@@ -43,7 +43,7 @@ int Card::get_strength(game_mode_t gamemode)
   }
 }
 
-int Card::get_points(game_mode_t gamemode)
+int Card::get_points(game_mode_t gamemode) const
 {
   switch (gamemode)
   {
@@ -58,7 +58,7 @@ int Card::get_points(game_mode_t gamemode)
 }
 
 // Requires at least 18 bytes of buffer
-int Card::print(char *buf)
+int Card::print(char *buf) const
 {
   const char *value_text = NULL;
   const char *suite_text = NULL;
@@ -104,3 +104,14 @@ bool operator!=(const Card lhs, const Card rhs)
   return (lhs.suite != rhs.suite || lhs.value != rhs.value);
 }
 
+card_suite_t game_mode_to_card_suite(game_mode_t game_mode)
+{
+  switch (game_mode)
+  {
+    case ClubsTrump: return Clubs;
+    case DiamondsTrump: return Diamonds;
+    case HeartsTrump: return Hearts;
+    case SpadesTrump: return Spades;
+    default: return InvalidSuite;
+  }
+}
index 00c6611e302cddbf5b254de500af09532fc7ceda..37d35c6b8f30c4ac99a994c765762ecc8d374904 100644 (file)
@@ -39,15 +39,17 @@ class Card
   public:
     Card();
     Card(card_suite_t suite_init, card_value_t value_init);
-    card_suite_t get_suite();
-    card_value_t get_value();
-    int get_strength(game_mode_t gamemode);
-    int get_points(game_mode_t gamemode);
-    int print(char *buf);
+    card_suite_t get_suite() const;
+    card_value_t get_value() const;
+    int get_strength(game_mode_t gamemode) const;
+    int get_points(game_mode_t gamemode) const;
+    int print(char *buf) const;
     friend bool operator==(const Card lhs, const Card rhs);
     friend bool operator!=(const Card lhs, const Card rhs);
 };
 
 extern const Card NO_CARD;
 
+card_suite_t game_mode_to_card_suite(game_mode_t game_mode);
+
 #endif
diff --git a/Examples/FreeRTOS/freertos_belot_sim/debug.hpp b/Examples/FreeRTOS/freertos_belot_sim/debug.hpp
new file mode 100644 (file)
index 0000000..e62adf4
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#define DEBUG
+
+extern SemaphoreHandle_t uart_mutex;
+
+#endif
\ No newline at end of file
index 47229c034213a6af625ecae32bc932b86a147aac..e8b7022d58e67f82e7c7a977fa4b03cd82aa8796 100644 (file)
@@ -99,9 +99,9 @@ int Deck::add_multiple(Card *cards_buf, size_t num_cards)
     return -2;
   }
 
-  if (end + num_cards > DECK_SIZE)
+  if (end + num_cards >= DECK_SIZE)
   {
-    size_t first_portion = num_cards - (end + num_cards) % DECK_SIZE;
+    size_t first_portion = num_cards - (end + 1 + num_cards) % DECK_SIZE;
     memcpy((void *) (cards + end), (const void *) cards_buf, first_portion * sizeof(Card));
     memcpy((void *) cards, (const void *) (cards_buf + first_portion), (num_cards - first_portion) * sizeof(Card));
   }
@@ -132,19 +132,14 @@ void Deck::shuffle()
   }
 }
 
+// Moving the start and end counters without copying any cards can have the same effect but I've implemented it this way for the sake of realism
 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
diff --git a/Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.cpp b/Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.cpp
new file mode 100644 (file)
index 0000000..d70cd0e
--- /dev/null
@@ -0,0 +1,140 @@
+#include "dumb_af_bot.hpp"
+#include "card.hpp"
+#include "belot.hpp"
+#include "belot_common.hpp"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+int dumb_af_bot_init(void *state, int8_t player_id, const Card *hand, const Card *trick, const Card *previous_trick, const bid_t *bids)
+{
+  if (state == NULL || hand == NULL || trick == NULL || previous_trick == NULL || bids == NULL)
+  {
+    return -1;
+  }
+
+  dumb_af_bot_state_t *st = (dumb_af_bot_state_t *) state;
+
+  st->player_id = player_id;
+  st->hand = hand;
+  st->trick = trick;
+  st->previous_trick = previous_trick;
+  st->bids = bids;
+  st->game_mode = AllPass;
+  st->all_trumps_announced = false;
+
+  return 0;
+}
+
+bid_t dumb_af_bot_bid(void *state)
+{
+  if (state == NULL)
+  {
+    return BidNone;
+  }
+
+  dumb_af_bot_state_t *st = (dumb_af_bot_state_t *) state;
+
+  // 30 godini TUES, 30 godini vsichko koz!
+  if (st->all_trumps_announced)
+  {
+    return BidPass;
+  }
+  for (uint8_t i = 0; i < NUM_PLAYERS; i++)
+  {
+    if (st->bids[i] == BidAllTrumps)
+    {
+      st->all_trumps_announced = true;
+      break;
+    }
+  }
+  if (st->all_trumps_announced)
+  {
+    return BidPass;
+  }
+  else
+  {
+    st->all_trumps_announced = true;
+    return BidAllTrumps;
+  }
+}
+
+int dumb_af_bot_choose_card(void *state)
+{
+  if (state == NULL)
+  {
+    return -1;
+  }
+
+  card_suite_t requested_suite = InvalidSuite;
+  dumb_af_bot_state_t *st = (dumb_af_bot_state_t *) state;
+  card_suite_t trump_suite = game_mode_to_card_suite(st->game_mode);
+  int8_t winning_card_id = st->player_id;
+
+  // Determine the requested suite
+  for (uint8_t i = 1; i < NUM_PLAYERS; i++)
+  {
+    if (st->trick[(st->player_id + i) % NUM_PLAYERS] != NO_CARD)
+    {
+      requested_suite = st->trick[(st->player_id + i) % NUM_PLAYERS].get_suite();
+      winning_card_id = find_winning_card(st->trick, (st->player_id + i) % NUM_PLAYERS, st->game_mode);
+      break;
+    }
+  }
+  // Determine the strenght of the strongest card so far
+  int winning_card_strenght = st->trick[winning_card_id].get_strength(st->game_mode);
+
+  if (requested_suite != InvalidSuite)
+  {
+    // Find the first card that matches the suite and is stronger
+    for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+    {
+      if (st->hand[i].get_suite() == requested_suite && st->hand[i].get_strength(st->game_mode) > winning_card_strenght)
+      {
+        return i;
+      }
+    }
+    // Just throw the first card that matches the suite
+    for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+    {
+      if (st->hand[i].get_suite() == requested_suite)
+      {
+        return i;
+      }
+    }
+    if (trump_suite != InvalidSuite)
+    {
+      // Find the first card that matches the trump suite and is strong enough
+      for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+      {
+        if (st->hand[i].get_suite() == trump_suite && st->hand[i].get_strength(st->game_mode) > winning_card_strenght)
+        {
+          return i;
+        }
+      }
+    }
+  }
+  // Either we don't have a matching card or we are first in turn. In that case just throw the first card we have.
+  for (uint8_t i = 0; i < CARDS_PER_PLAYER; i++)
+  {
+    if (st->hand[i] != NO_CARD)
+    {
+      return i;
+    }
+  }
+
+  // We're out of cards! This shouldn't happen.
+  return -2;
+}
+
+int dumb_af_bot_set_game_mode(void *state, game_mode_t game_mode)
+{
+  if (state == NULL)
+  {
+    return -1;
+  }
+
+  ((dumb_af_bot_state_t *) state)->game_mode = game_mode;
+
+  return 0;
+}
diff --git a/Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.hpp b/Examples/FreeRTOS/freertos_belot_sim/dumb_af_bot.hpp
new file mode 100644 (file)
index 0000000..a284ec4
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef DUMB_AF_BOT_H
+#define DUMB_AF_BOT_H
+
+#include "card.hpp"
+#include "belot.hpp"
+#include "belot_common.hpp"
+
+#include <stdint.h>
+
+#define DUMB_AF_BOT_STATE_SIZE sizeof(dumb_af_bot_state_t)
+
+typedef struct
+{
+  game_mode_t game_mode;
+  int8_t player_id;
+  const Card *hand;
+  const Card *trick;
+  const Card *previous_trick;
+  const bid_t *bids;
+  bool all_trumps_announced;
+  //other stuff
+}
+dumb_af_bot_state_t;
+
+int dumb_af_bot_init(void *state, int8_t player_id, const Card *hand, const Card *trick, const Card *previous_trick, const bid_t *bids);
+bid_t dumb_af_bot_bid(void *state);
+int dumb_af_bot_choose_card(void *state);
+int dumb_af_bot_set_game_mode(void *state, game_mode_t game_mode);
+
+#endif
\ No newline at end of file
index 070fdad1dbe309b9e54c59a24a533b17480acde2..709e6ebcf4040b983bc383e4a37dd32c507c8eb0 100644 (file)
@@ -2,6 +2,7 @@
 #include "belot_common.hpp"
 #include "card.hpp"
 #include "player.hpp"
+#include "dumb_af_bot.hpp"
 
 #include <stdlib.h>
 
 #define PLAYERS 6
 #define NOISE_PIN 34
 
-static void play_belot(__attribute__((unused)) void *args);
+typedef struct
+{
+  int (*bot_init)(void *state, int8_t player_id, const Card *hand, const Card *trick, const Card *previous_trick, const bid_t *bids);
+  bid_t (*bot_bid)(void *state);
+  int (*bot_choose_card)(void *state);
+  int (*bot_set_game_mode)(void *state, game_mode_t game_mode);
+  void *bot_state;
+}
+player_args_t;
+
+static void play_belot(void *pvParameters);
+static void think_really_hard(void);
 
 SemaphoreHandle_t uart_mutex = xSemaphoreCreateMutex();
 
 static Belot *lobby = NULL;
 static SemaphoreHandle_t lobby_mutex;
+
 static const char *player_names[PLAYERS] = {"Asen", "Boris", "Violeta", "Georgi", "Dimitar", "Elena"};
 static TaskHandle_t player_handles[PLAYERS];
+static const size_t bot_state_sizes[PLAYERS] = {DUMB_AF_BOT_STATE_SIZE, DUMB_AF_BOT_STATE_SIZE, DUMB_AF_BOT_STATE_SIZE, DUMB_AF_BOT_STATE_SIZE, DUMB_AF_BOT_STATE_SIZE, DUMB_AF_BOT_STATE_SIZE};
+static player_args_t player_args[PLAYERS] =
+{
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL},
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL},
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL},
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL},
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL},
+  {.bot_init = dumb_af_bot_init, .bot_bid = dumb_af_bot_bid, .bot_choose_card = dumb_af_bot_choose_card, .bot_set_game_mode = dumb_af_bot_set_game_mode, .bot_state = NULL}
+};
 
 void setup()
 {
@@ -39,7 +62,15 @@ void setup()
 
   for (uint8_t i = 0; i < PLAYERS; i++)
   {
-    if (xTaskCreate(play_belot, player_names[i], THREAD_STACK_SIZE, NULL, 1, &player_handles[i]) != pdPASS)
+    if (bot_state_sizes[i])
+    {
+      player_args[i].bot_state = malloc(bot_state_sizes[i]);
+      if (player_args[i].bot_state == NULL)
+      {
+        big_oof();
+      }
+    }
+    if (xTaskCreate(play_belot, player_names[i], THREAD_STACK_SIZE, (void *) &player_args[i], 1, &player_handles[i]) != pdPASS)
     {
       big_oof();
     }
@@ -49,9 +80,10 @@ void setup()
 void loop()
 {}
 
-void play_belot(__attribute__((unused)) void *args)
+static void play_belot(void *pvParameters)
 {
   Player player = Player(pcTaskGetName(NULL));
+  player_args_t *args = (player_args_t *) pvParameters;
 
   // Games loop
   while (true)
@@ -59,10 +91,8 @@ void play_belot(__attribute__((unused)) void *args)
     player.join_or_start_game(&lobby, lobby_mutex);
 
     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();
+    // The following methods on the Player class return pointers, so the bot can save them in its state. This way there is no need to pass many arguments to the other bot functions.
+    args->bot_init(args->bot_state, id, player.view_hand(), player.view_trick(), player.view_previous_trick(), player.view_bids());
 
     // Rounds loop
     while (true)
@@ -80,8 +110,7 @@ void play_belot(__attribute__((unused)) void *args)
       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);
+        player.bid(args->bot_bid(args->bot_state));
       }
       if (player.get_game_mode() == AllPass)
       {
@@ -93,15 +122,20 @@ void play_belot(__attribute__((unused)) void *args)
         player.deal_second();
       }
       player.collect_cards();
+      args->bot_set_game_mode(args->bot_state, player.get_game_mode());
       // 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);
+        think_really_hard();
+        player.play_card(args->bot_choose_card(args->bot_state));
       }
       player.end_round();
     }
     player.end_game();
   }
 }
+
+static void think_really_hard(void)
+{
+  vTaskDelay(1000);
+}
index b6719da145dd9d78c5246f6fab018b600a18b4a6..2d3d23c45408e151d628dbdba67f91e59254aebe 100644 (file)
@@ -3,6 +3,7 @@
 #include "belot.hpp"
 #include "belot_common.hpp"
 #include "card.hpp"
+#include "debug.hpp"
 
 #include "freertos/FreeRTOS.h"
 #include "freertos/semphr.h"
@@ -10,8 +11,6 @@
 
 #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.
@@ -49,6 +48,7 @@ int Player::join_or_start_game(Belot **lobby, SemaphoreHandle_t lobby_mutex)
     return -1;
   }
 
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
   Serial.println(" entered the lobby.");
@@ -56,6 +56,7 @@ int Player::join_or_start_game(Belot **lobby, SemaphoreHandle_t lobby_mutex)
   {
     big_oof();
   }
+  #endif
 
   while (player_id < 0)
   {
@@ -134,13 +135,18 @@ int8_t Player::get_dealer_id()
 
 void Player::split_deck()
 {
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
+  Serial.print("(");
+  Serial.print(player_id);
+  Serial.print(")");
   Serial.println(" splits the deck.");
   if (xSemaphoreGive(uart_mutex) != pdTRUE)
   {
     big_oof();
   }
+  #endif
   game->split_deck();
   end_turn();
 }
@@ -149,13 +155,18 @@ int Player::deal_two()
 {
   Card buf[2];
 
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
+  Serial.print("(");
+  Serial.print(player_id);
+  Serial.print(")");
   Serial.println(" deals 2 cards to each player.");
   if (xSemaphoreGive(uart_mutex) != pdTRUE)
   {
     big_oof();
   }
+  #endif
 
   for (uint8_t i = 0; i < NUM_PLAYERS; i++)
   {
@@ -176,13 +187,18 @@ int Player::deal_three()
 {
   Card buf[3];
 
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
+  Serial.print("(");
+  Serial.print(player_id);
+  Serial.print(")");
   Serial.println(" deals 3 cards to each player.");
   if (xSemaphoreGive(uart_mutex) != pdTRUE)
   {
     big_oof();
   }
+  #endif
 
   for (uint8_t i = 0; i < NUM_PLAYERS; i++)
   {
@@ -240,8 +256,12 @@ const bid_t *Player::view_bids()
 
 int Player::bid(bid_t bid)
 {
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
+  Serial.print("(");
+  Serial.print(player_id);
+  Serial.print(")");
   switch (bid)
   {
     case BidPass: Serial.println(": pass"); break;
@@ -259,6 +279,7 @@ int Player::bid(bid_t bid)
   {
     big_oof();
   }
+  #endif
   int error_code = game->bid(bid, player_id);
   if (!error_code)
   {
@@ -315,14 +336,19 @@ int Player::play_card(uint8_t card_num)
   }
 
   hand[card_num].print(card_text);
+  #ifdef DEBUG
   while (xSemaphoreTake(uart_mutex, portMAX_DELAY) != pdTRUE);
   Serial.print(name);
+  Serial.print("(");
+  Serial.print(player_id);
+  Serial.print(")");
   Serial.print(": ");
   Serial.println(card_text);
   if (xSemaphoreGive(uart_mutex) != pdTRUE)
   {
     big_oof();
   }
+  #endif
 
   int error_code = game->play_card(&hand[card_num], player_id);
   if (!error_code)