ITPW 2007
The official site of the contest (in Polish): http://www.mimuw.edu.pl/KNI/ITPW/
Note, that this program has been breed. Some of the parameters may look outright idiotic, but this is the strongest version I got. It does not do time control - no excessive computations done throughout the game.
/* Autor: Remigiusz Modrzejewski
* <lrem at maxnet org pl>
* Part of http://lrem.net/pages/view/itpw
* Distributed under the terms of GNU GPL version 2, as found on:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <list>
#include <vector>
#include <cassert>
using namespace std;
#define MAX_PLAYERS 4
#define MAX_CARD 13
#define MAX_SAME 8
#define MAX_POOL 104
#define POOL_LEVELS 4
#define WAR_POOL_LEVELS 3
#define SURE_THRESHOLD 15
#define HISTORY_FACTOR 0.531142595416039
#undef assert
#define max(a,b)(a<b?b:a)
#define min(a,b)(a<b?a:b)
struct _known_group
{
int top, bottom;
list<int> cards;
};
struct _log
{
int high,
low,
sum,
observations;
_log(void) :
high(0),
low(13),
sum(0),
observations(0)
{}
int mean(void)
{
return sum/observations;
}
};
struct player
{
int cards_in_hand,
top_card,
pending_decisions[MAX_POOL],
pd_bidders[MAX_POOL],
pd_pool[MAX_POOL],
pending_count,
cards_in_pool;
bool bidding,
its_me;
_log log[2][MAX_PLAYERS+1][WAR_POOL_LEVELS][POOL_LEVELS];
list<_known_group> known;
player() : its_me(false) {}
void play (void);
void move (void);
int after_war_decision(double);
int defend_decision(double);
int heads_up_decision(double);
void reveal (void);
void record (int);
void observe (void);
void draw (void);
void mark_winner (void);
void dispatch_war (void);
void win (void);
pair<double, double> probabilities (int); // higher, equal
pair<double, double> educated_guess (int); // guess, sureness
} players[MAX_PLAYERS];
struct game_status
{
int pool,
war_pool,
bidders,
bid,
players,
my_number,
unknowns[MAX_CARD+1],
known_pool[MAX_POOL],
known_count,
best_top;
bool know_top,
in_game,
winner_survives_war,
war;
player *winner;
void set_state (void);
} table;
void set_deck (void);
#ifdef DEBUG
#define debug(...)\
char deb_buf[BUFSIZ];\
sprintf(deb_buf, "%d.debug", table.my_number);\
FILE *deb_f=fopen(deb_buf, "a");\
fprintf(deb_f, __VA_ARGS__);\
fclose(deb_f);
void init_files (void)
{
char deb_buf[BUFSIZ];
sprintf(deb_buf, "%d.stderr", table.my_number);
stderr = fopen(deb_buf, "w");
sprintf(deb_buf, "%d.debug", table.my_number);
unlink(deb_buf);
}
void check_totals (void)
{
int total_hand = 0,
total_pool = 0;
for(int i = 0; i < table.players; i++)
{
assert(0 <= players[i].cards_in_hand);
total_hand += players[i].cards_in_hand;
assert(0 <= players[i].cards_in_pool);
total_pool += players[i].cards_in_pool;
}
assert(table.pool == total_pool + table.war_pool);
assert(total_hand + total_pool + table.war_pool == 104);
}
void check_types (void)
{
int of_type[MAX_CARD+1];
for(int i = 1; i <= MAX_CARD; i++)
of_type[i] = table.unknowns[i];
for(int i = 0; i < table.known_count; i++)
of_type[table.known_pool[i]]++;
for(int i = 0; i < table.players; i++)
{
int previous_top = -12345,
previous_bottom = -12345;
for(list<_known_group>::iterator it = players[i].known.begin();
it != players[i].known.end(); it++)
{
assert(previous_top < it->top);
assert(previous_bottom < it->top);
previous_top = it->top;
previous_bottom = it->bottom;
for(list<int>::iterator jt = it->cards.begin();
jt != it->cards.end(); jt++)
of_type[*jt]++;
}
}
for(int i = 1; i <= MAX_CARD; i++)
assert(of_type[i] == MAX_SAME);
}
void check_sanity (void)
{
check_totals();
check_types();
}
#else
#define debug(...)
#define assert(...)
#define init_files()
#define check_sanity()
#endif
int main(void)
{
srand(time(NULL));
while(true)
{
char command[BUFSIZ];
scanf("%s", command);
if(strcmp(command, "topcard") == 0)
{
int player;
scanf("%d", &player);
players[--player].reveal();
}
else if(strcmp(command,"your_number") == 0)
{
scanf("%d", &table.my_number);
table.my_number--;
players[table.my_number].its_me = true;
init_files();
printf("=OK\n\n");
}
else if(strcmp(command,"setdeck") == 0)
{
set_deck();
}
else if(strcmp(command,"setstate") == 0)
{
table.set_state();
}
else if (strcmp(command, "play") == 0)
{
int player;
scanf("%d", &player);
players[--player].play();
}
else if (strcmp(command, "genmove") == 0)
{
scanf("%*d");
players[table.my_number].move();
}
else
printf("?FTW\n\n");
#define MAX_SAME 8
scanf("%*[^\n]");
fflush(stdout);
}
}
void set_deck (void)
{
debug("set_deck\n");
int n, x;
scanf("%d", &n);
for(int i = 0; i <= MAX_CARD; i++)
table.unknowns[i] = 0;
for(int i =0; i < n; i++)
{
scanf("%d", &x);
table.unknowns[x]++;
}
for(int i = 0; i < table.players; i++)
players[i].known.clear();
table.know_top = table.war = table.in_game = false;
table.known_count = table.war_pool = 0;
table.winner = NULL;
printf("=OK\n\n");
}
void game_status::set_state (void)
{
debug("game_status::set_state war:%d\n", war);
if(!war && winner)
winner->win();
scanf("%d %d", &pool, &players);
bidders = 0;
bid = war ? 2 : 1;
for(int i = 0; i < players; i++)
{
player *current = &::players[i];
current->pending_count = 0;
int new_cih;
scanf("%d", &new_cih);
debug("\tplayer:%d cards_in_hand:%d new_cih:%d\n",
i, current->cards_in_hand, new_cih);
if(in_game)
war_pool += current->cards_in_pool;
current->cards_in_pool = in_game ?
current->cards_in_hand - new_cih : 1;
assert(!current->cards_in_pool
|| current->cards_in_pool == bid
|| !new_cih);
current->cards_in_hand = new_cih;
if(current->bidding = 0 < current->cards_in_pool)
{
bidders++;
}
}
know_top = !::players[my_number].bidding;
war = false;
in_game = true;
best_top = -1;
printf("=OK\n\n");
check_sanity();
}
void player::win (void)
{
_known_group tmp;
tmp.top = cards_in_hand + 1;
tmp.bottom = cards_in_hand + table.known_count;
for(int i = 0; i < table.known_count; i++)
tmp.cards.push_front(table.known_pool[i]);
table.known_count = 0;
known.push_back(tmp);
debug("player::win\n\tcards_in_hand:%d\n\ttable.pool:%d\n",
cards_in_hand, table.pool);
cards_in_hand += table.pool;
for(int i = 0; i < table.players; i++)
players[i].cards_in_pool = 0;
table.war_pool = table.pool = 0;
check_sanity();
}
void player::record (int decision)
{
pd_pool[pending_count] = cards_in_pool;
pd_bidders[pending_count] = table.bidders;
pending_decisions[pending_count++] = decision;
}
int bidders_level (int bidders)
{
if(bidders == 2)
return 0;
return 1;
}
int war_pool_level (int war_pool)
{
if(table.war_pool == 0)
return 0;
/*if(table.war_pool < 6)
return 1;*/
return 2;
}
int pool_level (int pool)
{
if(pool < 2)
return 0;
/*if(pool < 5)
return 1;*/
if(pool < 9 )
return 2;
return 3;
}
void player::observe (void)
{
if(!pending_count)
return;
while(pending_count--)
{
int i = pending_count;
_log *current = &log[pending_decisions[i]]
[bidders_level(pd_bidders[i])]
[war_pool_level(table.war_pool)]
[pool_level(pd_pool[i])];
current->high = max(current->high, top_card);
current->low = min(current->low, top_card);
current->sum += top_card;
current->observations++;
}
}
void player::play (void)
{
char move[BUFSIZ];
scanf("%s", move);
if(strcmp(move, "pass") == 0)
{
pending_count = 0;
bidding = false;
table.bidders--;
draw();
}
else if(strcmp(move, "raise") == 0)
{
int count;
scanf("%d", &count);
table.bid += count;
record(1);
}
else
{
record(0);
}
if(bidding)
{
int change = min(table.bid - cards_in_pool, cards_in_hand);
table.pool += change;
cards_in_hand -= change;
cards_in_pool += change;
}
printf("=OK\n\n");
check_sanity();
}
void player::mark_winner (void)
{
table.war = false;
table.best_top = top_card;
table.winner = this;
table.winner_survives_war = 0 < cards_in_hand;
}
void player::dispatch_war (void)
{
if(table.winner_survives_war)
table.war = table.war || 0 < cards_in_hand;
else
if(cards_in_hand)
mark_winner();
else
table.winner = NULL;
}
void player::reveal (void)
{
scanf("%d", &top_card);
debug("player::reveal top_card:%d table.best_top:%d\n",
top_card, table.best_top);
observe();
if(table.know_top)
{
draw();
if(!its_me)
{
table.unknowns[top_card]--;
assert(0 <= table.unknowns[top_card]);
table.known_pool[table.known_count++] = top_card;
}
if(top_card > table.best_top)
{
mark_winner();
}
else if(top_card == table.best_top)
{
dispatch_war();
}
}
else
{
table.unknowns[top_card]--;
table.known_pool[table.known_count++] = top_card;
table.know_top = true;
}
printf("=OK\n\n");
check_sanity();
}
void player::draw()
{
debug("player::draw\n\tknown.front().top:%d\n\tcards_in_pool:%d\n",
known.front().top, cards_in_pool);
for(list<_known_group>::iterator it = known.begin();
it != known.end(); it++)
{
it->top -= cards_in_pool;
it->bottom -= cards_in_pool;
}
while(!known.empty() && known.front().top <= 0)
{
for(list<int>::iterator it = known.front().cards.begin();
it != known.front().cards.end(); it++)
{
table.unknowns[*it]++;
assert(table.unknowns[*it] <= MAX_SAME);
}
known.pop_front();
}
check_sanity();
}
pair<double, double> player::probabilities (int opposed_to) // higher, equal
{
int higher_unknowns = 0,
equal_unknowns = 0,
lower_unknowns = 0,
total_unknowns;
for(int i = 0; i < opposed_to; i++)
lower_unknowns += table.unknowns[i];
equal_unknowns = table.unknowns[opposed_to];
for(int i = opposed_to + 1; i <= MAX_CARD; i++)
higher_unknowns += table.unknowns[i];
total_unknowns = higher_unknowns + equal_unknowns + lower_unknowns;
debug("player::probabilities\n\topposed_to:%2d\n\thigher_unknowns:%3d"
"\n\tequal_unknowns:%3d\n\tlower_unknowns:%3d\n",
opposed_to, higher_unknowns, equal_unknowns,
lower_unknowns);
return make_pair(((double)higher_unknowns)/total_unknowns,
((double)equal_unknowns)/total_unknowns);
}
pair <double, double> player::educated_guess (int opposed_to) // guess, sureness
{
int observations = 0;
double sureness,
probability = 0;
for(int i = 0; i < pending_count; i++)
{
_log *current = &log[pending_decisions[i]]
[bidders_level(pd_bidders[i])]
[war_pool_level(table.war_pool)]
[pool_level(pd_pool[i])];
if(opposed_to < current->low)
probability += 1.0;
else
if(opposed_to <= current->high)
probability += (current->mean() - opposed_to)
/ MAX_CARD * 0.5 + 0.5;
observations += current->observations;
}
if(pending_count)
probability /= pending_count;
sureness = (double)observations / SURE_THRESHOLD;
if(sureness > 1.0)
sureness = 1.0;
debug("player::educated_guess probability:%f sureness:%f\n",
probability, sureness);
return make_pair(probability, sureness);
}
int player::after_war_decision (double not_lose_probability)
{
return 0 + (not_lose_probability > 0.376839497753451 && cards_in_pool < 3)
- (not_lose_probability < 0.573746084050261 && cards_in_pool > 2);
}
int player::defend_decision (double not_lose_probability)
{
return -1 + (not_lose_probability > 0.925840075024843
&& cards_in_hand > 19)
+ (not_lose_probability > 0.72379728518924
&& cards_in_hand > 25
&& !(rand()%1) );
}
int player::heads_up_decision (double not_lose_probability)
{
return -1 + (not_lose_probability > 0.915249272223223
&& cards_in_hand > 25)
+ (not_lose_probability > 0.749845951323045
&& cards_in_hand > 31
&& !(rand()%6) );
}
void player::move (void)
{
int decision = -1;
double win_probability,
historical_probability;
historical_probability = win_probability = 1;
for(int i = 0; i < table.players; i++)
if(players[i].bidding && !players[i].its_me)
{
double current_not_win;
pair<double, double> current_historical,
current_statistical;
current_statistical =
players[i].probabilities(top_card);
current_not_win = current_statistical.first
+ current_statistical.second;
win_probability -= current_not_win;
current_historical= players[i].educated_guess(top_card);
historical_probability -= (current_historical.first
* current_historical.second) +
(current_not_win
* (1.0 - current_historical.second));
}
win_probability = historical_probability * HISTORY_FACTOR
+ win_probability * (1 - HISTORY_FACTOR);
if(win_probability > 0.917373993954353 || top_card == 13)
decision = cards_in_hand < 14 ? 0 :
(3 - !(rand()%1) - !(rand()%3));
else if(top_card > 7 && table.bid == 1 && rand()%3)
{
decision = 1;
}
else
if(table.war_pool)
{
decision = after_war_decision(win_probability);
}
else if (table.bidders == 2)
{
decision = heads_up_decision(win_probability);
}
else
{
decision = defend_decision (win_probability);
}
debug("player::move\n\ttop_card:%2d\n\twin_probability:%f\n\t"
"decision:%d\n",
top_card, win_probability, decision);
if(cards_in_pool == table.bid)
decision = max(decision, 0);
decision = min(decision, max(0,(
(cards_in_hand + cards_in_pool - table.bid))));
if(cards_in_hand == 0)
decision = 0;
if(decision > 0)
printf("=raise %d\n\n", decision);
else if(decision < 0)
printf("=pass\n\n");
else
printf("=check\n\n");
}