Skip to content

Commit

Permalink
bot fixed(?) but still needs additional work...
Browse files Browse the repository at this point in the history
Needs code to not perform same command twice if deck size hasn't changed. This means previous command was rejected. If it does produce same code twice, "continue" to force bot to move onto another command.
  • Loading branch information
r3w0p committed May 26, 2024
1 parent e197ea8 commit 76dc3c2
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 237 deletions.
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ add_library(model
target_link_libraries(model core)

add_library(user
"include/caravan/user/bot_easy.h"
"include/caravan/user/user.h"
"include/caravan/user/bot/normal.h"

"src/caravan/user/bot_easy.cpp"
"src/caravan/user/user.cpp"
"src/caravan/user/bot/normal.cpp"
)

add_library(view
Expand Down
4 changes: 4 additions & 0 deletions include/caravan/core/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,8 @@ bool is_numeral_card(Card c);

bool is_face_card(Card c);

std::string caravan_letter_to_str(CaravanName caravan_name);

uint8_t numeral_card_rank_value(Card c);

#endif //CARAVAN_CORE_COMMON_H
20 changes: 20 additions & 0 deletions include/caravan/user/bot/normal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2022-2024 r3w0p
// The following code can be redistributed and/or
// modified under the terms of the GPL-3.0 License.

#ifndef CARAVAN_USER_BOT_NORMAL_H
#define CARAVAN_USER_BOT_NORMAL_H


#include "caravan/user/user.h"

class UserBotNormal : public User {
public:
explicit UserBotNormal(PlayerName pn) : User(pn) {};

bool is_human() override;
std::string request_move(Game *g) override;
void close() override;
};

#endif //CARAVAN_USER_BOT_NORMAL_H
20 changes: 0 additions & 20 deletions include/caravan/user/bot_easy.h

This file was deleted.

4 changes: 2 additions & 2 deletions include/caravan/user/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class User {

PlayerName get_name() { return name; }
virtual bool is_human() = 0;
virtual GameCommand generate_option(Game *g) = 0;
virtual std::string request_move(Game *g) = 0;
};

class UserHuman : public User {
public:
explicit UserHuman(PlayerName pn) : User(pn) {};

bool is_human() override;
GameCommand generate_option(Game *g) override;
std::string request_move(Game *g) override;
void close() override;
};

Expand Down
47 changes: 47 additions & 0 deletions src/caravan/core/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// modified under the terms of the GPL-3.0 License.

#include "caravan/core/common.h"
#include "caravan/core/exceptions.h"

bool is_numeral_card(Card c) {
return (c.rank >= ACE and c.rank <= TEN);
Expand All @@ -11,3 +12,49 @@ bool is_numeral_card(Card c) {
bool is_face_card(Card c) {
return (c.rank >= JACK and c.rank <= JOKER);
}

std::string caravan_letter_to_str(CaravanName caravan_name) {
switch (caravan_name) {
case CARAVAN_A:
return "A";
case CARAVAN_B:
return "B";
case CARAVAN_C:
return "C";
case CARAVAN_D:
return "D";
case CARAVAN_E:
return "E";
case CARAVAN_F:
return "F";
default:
return "";
}
}

uint8_t numeral_card_rank_value(Card c) {
switch (c.rank) {
case ACE:
return 1;
case TWO:
return 2;
case THREE:
return 3;
case FOUR:
return 4;
case FIVE:
return 5;
case SIX:
return 6;
case SEVEN:
return 7;
case EIGHT:
return 8;
case NINE:
return 9;
case TEN:
return 10;
default:
throw CaravanFatalException("Card is not a numeral.");
}
}
8 changes: 4 additions & 4 deletions src/caravan/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <iostream>
#include <cxxopts.hpp>
#include "caravan/view/view_tui.h"
#include "caravan/user/bot_easy.h"
#include "caravan/user/bot/normal.h"

const std::string ARG_PVP = "pvp";
const std::string ARG_BOTS = "bots";
Expand Down Expand Up @@ -73,12 +73,12 @@ int main(int argc, char *argv[]) {
user_def = new UserHuman(PLAYER_DEF);

} else if (bots) { // bot vs bot
user_abc = new UserBotEasy(PLAYER_ABC);
user_def = new UserBotEasy(PLAYER_DEF);
user_abc = new UserBotNormal(PLAYER_ABC);
user_def = new UserBotNormal(PLAYER_DEF);

} else { // humans vs bot
user_abc = new UserHuman(PLAYER_ABC);
user_def = new UserBotEasy(PLAYER_DEF);
user_def = new UserBotNormal(PLAYER_DEF);
}

GameConfig config = {
Expand Down
177 changes: 177 additions & 0 deletions src/caravan/user/bot/normal.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright (c) 2022-2024 r3w0p
// The following code can be redistributed and/or
// modified under the terms of the GPL-3.0 License.

#include <random>
#include "caravan/user/bot/normal.h"

const std::string PLAY = "P";
const std::string DISCARD = "D";
const std::string CLEAR = "C";


uint8_t pos_card_numeral(Player *p) {
uint8_t size_hand = p->get_size_hand();
Hand h = p->get_hand();

for (int i = 0; i < size_hand; ++i) {
if (is_numeral_card(h[i])) {
return i + 1;
}
}

return 0;
}

/*
* PUBLIC
*/

bool UserBotNormal::is_human() {
return false;
}

void UserBotNormal::close() {
if (!closed) {
closed = true;
}
}

std::string UserBotNormal::request_move(Game *g) {
if (g->is_closed()) { throw CaravanFatalException("The game has already closed."); }

Player *me = g->get_player(name);
uint8_t my_hand_size = me->get_size_hand();

if (my_hand_size == 0) { throw CaravanFatalException("Bot has an empty hand."); }

PlayerCaravanNames my_caravans = g->get_player_caravan_names(name);
PlayerCaravanNames opp_caravans = g->get_player_caravan_names(name == PLAYER_ABC ? PLAYER_DEF : PLAYER_ABC);

uint16_t my_move_count = me->get_moves_count();

if (my_move_count < MOVES_START_ROUND) {
// Add numeral cards for start round
uint8_t pos_hand = pos_card_numeral(me);
if (pos_hand == 0) { throw CaravanFatalException("Bot does not have enough numeral cards to finish the Start round."); }

switch(my_move_count) {
case 0:
return PLAY + std::to_string(pos_hand) + caravan_letter_to_str(my_caravans[0]);
case 1:
return PLAY + std::to_string(pos_hand) + caravan_letter_to_str(my_caravans[1]);
case 2:
return PLAY + std::to_string(pos_hand) + caravan_letter_to_str(my_caravans[2]);
default:
throw CaravanFatalException("Bot cannot be in Start state after its first 3 moves have been made.");
}

} else {
// After start round
Table *table = g->get_table();

// Clear any caravans that are bust or full of cards
for (uint8_t i_cvn = 0; i_cvn < PLAYER_CARAVANS_MAX; i_cvn++) {
uint16_t cvn_bid = table->get_caravan(my_caravans[i_cvn])->get_bid();
uint8_t cvn_size = table->get_caravan(my_caravans[i_cvn])->get_size();

if (cvn_bid > CARAVAN_SOLD_MAX || cvn_size == TRACK_NUMERIC_MAX) {
return CLEAR + caravan_letter_to_str(my_caravans[i_cvn]);
}
}

// Otherwise, cycle through cards in hand
for (uint8_t pos_hand = 1; pos_hand <= my_hand_size; pos_hand++) {
Card c_hand = me->get_from_hand_at(pos_hand);

if (is_numeral_card(c_hand)) {
// If numeral, look through caravans
for (uint8_t i = 0; i < PLAYER_CARAVANS_MAX; ++i) {
Caravan *my_cvn = table->get_caravan(my_caravans[i]);
Caravan *opp_cvn = table->get_caravan(opp_caravans[i]);

std::string move_play_numeral = PLAY + std::to_string(pos_hand) + caravan_letter_to_str(my_cvn->get_name());

uint16_t my_cvn_bid = my_cvn->get_bid();
uint16_t opp_cvn_bid = opp_cvn->get_bid();

// Skip caravan if sold and winning
if (my_cvn_bid >= CARAVAN_SOLD_MIN &&
my_cvn_bid <= CARAVAN_SOLD_MAX &&
(my_cvn_bid > opp_cvn_bid || opp_cvn_bid > CARAVAN_SOLD_MAX)) {
continue;
}

uint8_t my_cvn_size = my_cvn->get_size();

// Skip caravan if full
if (my_cvn_size == TRACK_NUMERIC_MAX) {
continue;
}

// Play numeral if caravan is empty
if (my_cvn_size == 0) {
return move_play_numeral;
}

// Not empty, so check the top slot in the caravan
Slot slot_top = my_cvn->get_slot(my_cvn_size);

// Ignore numeral if cards have same rank
if (slot_top.card.rank == c_hand.rank) {
continue;
}

bool not_bust = (my_cvn_bid + numeral_card_rank_value(c_hand)) <= CARAVAN_SOLD_MAX;

// Same suit as caravan and numeral would not cause bust
if (my_cvn->get_suit() == c_hand.suit && not_bust) {
return move_play_numeral;
}

// Not same suit, check direction
Direction my_cvn_dir = my_cvn->get_direction();

// Card ascending with caravan and would not bust
if (my_cvn_dir == ASCENDING && c_hand.rank > slot_top.card.rank && not_bust) {
return move_play_numeral;
}

// Card descending with caravan and would not bust
if (my_cvn_dir == DESCENDING && c_hand.rank < slot_top.card.rank && not_bust) {
return move_play_numeral;
}
}

} else {
uint8_t i_opp_cvn_most_cards = PLAYER_CARAVANS_MAX;
uint8_t n_opp_cvn_most_cards = 0;

// Put face card on opp caravan with the most cards
for (uint8_t i = 0; i < PLAYER_CARAVANS_MAX; ++i) {
uint8_t size_cvn = table->get_caravan(opp_caravans[i])->get_size();

if (size_cvn > n_opp_cvn_most_cards) {
i_opp_cvn_most_cards = i;
n_opp_cvn_most_cards = size_cvn;
}
}

// If there exists an opp caravan with at least 1 card on it
if (i_opp_cvn_most_cards < PLAYER_CARAVANS_MAX) {
Caravan *opp_cvn = table->get_caravan(opp_caravans[i_opp_cvn_most_cards]);

uint8_t opp_cvn_size = opp_cvn->get_size();
Slot opp_slot_top = table->get_caravan(opp_caravans[i_opp_cvn_most_cards])->get_slot(opp_cvn_size);

if (opp_slot_top.i_faces < TRACK_FACE_MAX) {
return PLAY + std::to_string(pos_hand) + caravan_letter_to_str(opp_cvn->get_name()) + std::to_string(opp_cvn_size);
}
}
}
}
}

// Bot cannot make any of the other moves, so discard first card in hand
return DISCARD + "1";
}
Loading

0 comments on commit 76dc3c2

Please sign in to comment.