treewars/gamedata.cpp

312 lines
6.6 KiB
C++
Raw Normal View History

#include "gamedata.h"
#include "mathutils.h"
#include "debug.h"
#include <list>
#include <algorithm>
2011-06-24 15:37:56 +00:00
#include <cassert>
using std::list;
int GameData::PLAYER1_COLOUR = 0x4a483f;
int GameData::PLAYER2_COLOUR = 0x090c7a;
int GameData::BASE_BUILD_RADIUS = 75;
int GameData::NODE_RADIUS = 10;
GameVertex::GameVertex(int x, int y, int z, int r, int colour, int score,
VertexType type, Player* player)
: Vertex(x, y, z, r, colour, score)
{
this->type = type;
this->player = player;
}
GameData::GameData()
: Graph(true)
{
current = NULL;
mode = MODE_BUILD;
player1 = Player("player 1", PLAYER1_COLOUR);
player2 = Player("player 2", PLAYER2_COLOUR);
turn = &player1;
}
GameData::~GameData() { }
Vertex* GameData::get_current_vertex(bool only_mine) const
{
if (only_mine)
{
if (current != NULL &&
current->colour == turn->get_colour()) return current;
return NULL;
}
return current;
}
void GameData::clear_current_vertex()
{
mode = MODE_SELECT;
current = NULL;
}
void GameData::toggle_turn()
{
if (!turn->has_played()) turn->set_played();
if (!endgame())
{
if (turn == &player1) turn = &player2;
else if (turn == &player2) turn = &player1;
if (!turn->has_played()) mode = MODE_BUILD;
else mode = MODE_SELECT;
}
current = NULL;
}
void GameData::handle_click(int x, int y)
{
int r = 10;
int colour;
colour = turn->get_colour();
if (mode == MODE_SELECT)
{
// select_vertex handles making sure a point exists at (x,y)
select_vertex(x, y);
}
else if (mode == MODE_BUILD)
{
add_vertex(x, y, 0, r, colour);
}
else if (mode == MODE_ATTACK)
{
Vertex* v = vertex_at(x, y, 0);
if (v == NULL || v->colour == turn->get_colour()) return;
if (v->colour != colour) attack_vertex(v);
}
}
2011-07-01 18:39:45 +00:00
bool GameData::select_vertex(int x, int y, bool only_mine)
{
Vertex * v = vertex_at(x, y, 0);
2011-07-01 18:39:45 +00:00
if (v == NULL) return false;
if (only_mine && v->colour == turn->get_colour())
{
current = v;
2011-07-01 18:39:45 +00:00
return true;
}
current = v;
2011-07-01 18:39:45 +00:00
return true;
}
bool GameData::add_vertex(int x, int y, int z, int r, int colour)
{
GameVertex* v = new GameVertex(x, y, z, r, colour, 10, VERTEX_ATTACKER,
turn);
if (current == NULL)
{
// this is the special case for adding the first vertex for each player
if (!turn->has_played())
{
if (Graph::add_vertex(v))
{
#ifdef DEBUG
fprintf(stderr, "debug: GameData::add_vertex(): strength=%2.f\n", calculate_strength(*(vertices.rbegin())));
#endif
toggle_turn();
return true;
}
}
// really, we shouldn't be able to get here. return false just in case
delete v;
return false;
}
// same here - just a logic check
if (current->colour != turn->get_colour())
{
delete v;
return false;
}
// This is the range check...
if (MathUtils::distance(current->x, current->y, 0, v->x, v->y, 0) >
get_range())
{
delete v;
return false;
}
if (Graph::add_vertex(v, current))
{
#ifdef DEBUG
fprintf(stderr, "debug: GameData::add_vertex(): strength=%.2f\n",
calculate_strength(*(vertices.rbegin())));
#endif
toggle_turn();
return true;
}
delete v;
return false;
}
float GameData::calculate_armor(Vertex* node)
{
float str = calculate_strength(node);
return str / 10;
}
float GameData::calculate_strength(Vertex* node)
{
list<Vertex*> visited;
// Special case - a one-node tree just returns its own score!
2011-07-01 15:48:49 +00:00
if (node->neighbors.empty()) return (float)node->score;
return calculate_strength_r(node, 0, visited);
}
// Oh the recursive recursion!
float GameData::calculate_strength_r(Vertex* node, unsigned int depth, list<Vertex*>& visited)
{
// Find which vertices we need to visit from here
list<Vertex*> neighbors = node->neighbors;
list<Vertex*> to_visit;
2011-06-24 15:37:56 +00:00
visited.push_back(node);
2011-06-24 16:18:52 +00:00
for (list<Vertex*>::iterator cursor = neighbors.begin();
cursor != neighbors.end(); cursor++)
{
2011-06-24 16:18:52 +00:00
Vertex* v = *cursor;
// if this is true, we haven't visited the vertex on the other end of
// this edge yet
2011-06-24 16:18:52 +00:00
if (find(visited.begin(), visited.end(), v) == visited.end())
{
2011-06-24 16:18:52 +00:00
to_visit.push_back(v);
}
}
// This is the base case - this node has no unvisited neighbors
if (to_visit.empty())
{
2011-06-24 15:37:56 +00:00
assert(depth > 0);
return (float)(node->score) / depth;
}
// Else, iterate through to_visit and visit them all, summing their
// effective strengths adjusted for depth.
// Since our trees are acyclic, this can't loop.
float modscore = (float)node->score;
if (depth > 0) modscore /= depth;
for (list<Vertex*>::iterator cursor = to_visit.begin();
cursor != to_visit.end(); cursor++)
{
Vertex* v = *cursor;
modscore += calculate_strength_r(v, depth+1, visited);
}
return modscore;
}
// This class contains logic checks to keep the mode aligned with
// what is reasonable. Special cases inside the GameData class should just
// do mode = MODE_<whatever>
void GameData::set_mode(Mode m)
{
// Stay in MODE_SELECT (or maybe MODE_BUILD) when current is null
if (current == NULL) return;
// The other modes all require current to match the player
if (current->colour == turn->get_colour()) mode = m;
}
2011-06-24 16:18:52 +00:00
int GameData::get_range(Vertex* node)
{
2011-06-24 16:18:52 +00:00
if (node == NULL) node = current;
if (node == NULL) return 0;
else if (mode == MODE_MOVE) return turn->get_energy();
else if (mode == MODE_BUILD) return BASE_BUILD_RADIUS;
2011-06-24 16:18:52 +00:00
else if (mode == MODE_ATTACK)
{
int range = BASE_BUILD_RADIUS;
list<Vertex*> neighbors = node->neighbors;
for(list<Vertex*>::iterator cursor = neighbors.begin();
cursor != neighbors.end(); cursor++)
{
Vertex* v = *cursor;
range -= (100 - MathUtils::distance(v->x, v->y, v->z,
node->x, node->y, node->z))
/ 2;
}
if (range < 0) range = 0;
return range;
2011-06-24 16:18:52 +00:00
}
}
void GameData::attack_vertex(Vertex* target)
{
float atk = calculate_strength(current);
float armor = calculate_armor(current);
2011-06-24 16:18:52 +00:00
int damage = (int)(atk / armor);
target->score -= damage;
if (target->score <= 0) remove_vertex(target);
#ifdef DEBUG
fprintf(stderr, "debug: GameData::attack_vertex(): atk=%.2f, armor=%.2f, armor=%.2f, damage=%d\n", atk, armor, damage);
#endif
toggle_turn();
}
bool GameData::endgame()
{
if (!(player1.has_played() && player2.has_played())) return false;
if (get_colour(player1.get_colour()).empty())
{
2011-06-29 02:14:55 +00:00
debug("Gamedata::endgame(): player 2 wins\n");
return true;
}
if (get_colour(player2.get_colour()).empty())
{
2011-06-29 02:14:55 +00:00
debug("Gamedata::endgame(): player 1 wins\n");
return true;
}
return false;
}