From 85b09864f6d489e8c998e9f172d25079d572c602 Mon Sep 17 00:00:00 2001 From: manuel Date: Mon, 18 Apr 2011 19:47:34 +0200 Subject: - add actor.canEat(other) to check if actor can eat other (note: that doesn't mean that other can eat actor!!) - server now generated and sends the colorlist to the server in the first map update packet - add a better AI --- pacman-c++/actor.cpp | 9 +++ pacman-c++/actor.h | 1 + pacman-c++/constants.h | 11 ++++ pacman-c++/mainwidget.cpp | 12 +++- pacman-c++/pacman.proto | 5 +- pacman-c++/sceneholder.cpp | 10 ++++ pacman-c++/sceneholder.h | 9 +++ pacman-c++/server.cpp | 145 ++++++++++++++++++++++++++++++++++++--------- pacman-c++/server.h | 2 +- 9 files changed, 170 insertions(+), 34 deletions(-) (limited to 'pacman-c++') diff --git a/pacman-c++/actor.cpp b/pacman-c++/actor.cpp index 9ff51f3..cf3fc13 100644 --- a/pacman-c++/actor.cpp +++ b/pacman-c++/actor.cpp @@ -193,6 +193,15 @@ bool Actor::isMoving() return (m_moving->state() == QAbstractAnimation::Running); } +bool Actor::canEat(Actor *other, const QList &order) +{ + if (other == NULL || order.empty() || m_color == other->color()) + return false; + + int idx = order.indexOf(m_color); + return (order.at(idx + 1) == other->color()); +} + void Actor::die() { if (!m_local) diff --git a/pacman-c++/actor.h b/pacman-c++/actor.h index 902951b..41ad232 100644 --- a/pacman-c++/actor.h +++ b/pacman-c++/actor.h @@ -42,6 +42,7 @@ public: void eatingPacman(); void startEating(); void stopEating(); + bool canEat(Actor *other, const QList &order); unsigned int getRoundPoints(); unsigned int getGamePoints(); diff --git a/pacman-c++/constants.h b/pacman-c++/constants.h index dc72611..20a0e15 100644 --- a/pacman-c++/constants.h +++ b/pacman-c++/constants.h @@ -41,6 +41,17 @@ namespace Constants { const unsigned int colorize_block_tickcount_min = 50; const unsigned int colorize_block_tickcount_max = 100; } + + namespace AI + { + /* bots minimum manhatten distance before other players will be recognized */ + const unsigned int player_minimum_distance = 4; + /* weight values used to determine new direction of bot */ + const unsigned int weight_afraid = 50; + const unsigned int weight_hunt = 10; + const unsigned int weight_bonus_point = 3; + const unsigned int weight_point = 1; + } } namespace Color diff --git a/pacman-c++/mainwidget.cpp b/pacman-c++/mainwidget.cpp index c674c55..42abd01 100644 --- a/pacman-c++/mainwidget.cpp +++ b/pacman-c++/mainwidget.cpp @@ -6,7 +6,8 @@ #include "pacman.pb.h" MainWidget::MainWidget(QWidget *parent) - : QWidget(parent), m_currentKey(Transmission::none), m_running(false), m_maxplayers(0) + : QWidget(parent), m_currentKey(Transmission::none), m_running(false), m_scene(NULL), + m_maxplayers(0) { /* create audio player */ m_ambientPlayer = new GaplessAudioPlayer(Sound::Ambient, 100, this); @@ -146,6 +147,15 @@ void MainWidget::tick() bool worked = m_updatepacket.ParseFromArray(data->data(), data->size()); Q_ASSERT(worked); Q_UNUSED(worked); + + if (m_updatepacket.eating_order_size() > 0) + { + Q_ASSERT(m_scene != NULL); + QList order; + for(int i = 0; i < m_updatepacket.eating_order_size(); ++i) + order.append(static_cast(m_updatepacket.eating_order(i) & Transmission::color_mask)); + } + Transmission::map_t map = Util::createUninitialisedMap(); Q_ASSERT(m_updatepacket.field_size() == (int) (Constants::map_size.width * Constants::map_size.height)); int i = 0; diff --git a/pacman-c++/pacman.proto b/pacman-c++/pacman.proto index 51bb239..8e088e8 100644 --- a/pacman-c++/pacman.proto +++ b/pacman-c++/pacman.proto @@ -9,13 +9,10 @@ message Init { required uint32 maxplayers = 2; } -message MapInit { - repeated uint32 field = 1 [packed=true]; -} - message MapUpdate { repeated uint32 field = 1 [packed=true]; repeated uint32 round_points = 2; repeated uint32 game_points = 3; + repeated uint32 eating_order = 4; } diff --git a/pacman-c++/sceneholder.cpp b/pacman-c++/sceneholder.cpp index 578b0df..dace711 100644 --- a/pacman-c++/sceneholder.cpp +++ b/pacman-c++/sceneholder.cpp @@ -197,6 +197,16 @@ void SceneHolder::decrementPoints() --m_pointsLeft; } +void SceneHolder::setEatingOrder(QList &order) +{ + m_eatingorder = order; +} + +QList &SceneHolder::eatingOrder() +{ + return m_eatingorder; +} + QPoint SceneHolder::mapPositionToCoord(unsigned int x, unsigned int y) { return QPoint(x * Constants::field_size.width, y * Constants::field_size.height); diff --git a/pacman-c++/sceneholder.h b/pacman-c++/sceneholder.h index 418f16b..69ee598 100644 --- a/pacman-c++/sceneholder.h +++ b/pacman-c++/sceneholder.h @@ -21,6 +21,8 @@ public: void updateMap(const Transmission::map_t& map, const unsigned int x, const unsigned int y); void setColor(Color::Color color = Color::none); Color::Color color(); + void setEatingOrder(QList &order); + QList &eatingOrder(); private slots: void decrementPoints(); @@ -46,6 +48,13 @@ protected: /* my local color */ Color::Color m_color; + /* a actor can only eat his upper neighbour + * the order of the neighbours is determined by the colors order (sent from server) + * please note that !pl1.canEat(pl2, ...) doesn't necessarily mean that pl2 can eat pl1 + * order MUST be in this format: [col1, [col2, ..., colN], col1] + */ + QList m_eatingorder; + /* points left before round ends */ unsigned int m_pointsLeft; }; diff --git a/pacman-c++/server.cpp b/pacman-c++/server.cpp index 65a8f8b..4219a43 100644 --- a/pacman-c++/server.cpp +++ b/pacman-c++/server.cpp @@ -19,6 +19,11 @@ Server::Server(QWidget *parent) bool Server::run() { + /* create eating order list first - this can also be created dynamically per round (theoretically) */ + for(unsigned i = 0; i < m_maxplayers; ++i) + m_eatingorder.append(Color::order[i]); + m_eatingorder.append(Color::order[0]); + qDebug() << "[Server] Running server..."; qDebug() << "[Server] Max players:" << m_maxplayers; qDebug() << "[Server] Number of bots:" << m_numbots; @@ -42,7 +47,7 @@ bool Server::run() } updateMap(map); - sendUpdate(map); + sendUpdate(map, true); Util::deleteMap(map); map = NULL; @@ -55,7 +60,7 @@ bool Server::run() void Server::tick() { //qDebug() << "[Tick] Doing server update"; - foreach(Color::Color color, m_bots) + foreach (Color::Color color, m_bots) botCalculate(m_actors[color]); Transmission::map_t map = calculateUpdates(); updateMap(map); @@ -218,7 +223,7 @@ void Server::colorizeBlocks(Transmission::map_t map) { /* check for actor collision */ bool skip = false; - foreach(Actor *actor, m_actors) + foreach (Actor *actor, m_actors) { if (CoordToMapPosition(actor->pos().toPoint()) == i.key()) skip = true; @@ -272,6 +277,7 @@ bool Server::waitForClientConnections() qDebug() << "[Server] Waiting for clients"; ProtoBuf::Init packet; + packet.set_maxplayers(m_maxplayers); for (unsigned int i = 0; i < (m_maxplayers - m_numbots); ++i) { bool connectionAvailable = tcpSrv->waitForNewConnection(-1); @@ -284,7 +290,6 @@ bool Server::waitForClientConnections() Color::Color color = Color::order[i]; m_clientConnections[color] = socket; packet.set_color(color); - packet.set_maxplayers(m_maxplayers); Util::sendPacket(packet, socket); qDebug() << "[Connect] New Player: color=" << color; } @@ -299,7 +304,7 @@ bool Server::waitForClientConnections() return true; } -void Server::sendUpdate(Transmission::map_t map) +void Server::sendUpdate(Transmission::map_t map, bool firstPacket) { m_updatepacket.Clear(); @@ -315,6 +320,13 @@ void Server::sendUpdate(Transmission::map_t map) m_updatepacket.add_game_points(m_actors.value(Color::order[i])->getGamePoints()); } + /* we send the eating_order inside the first packet */ + if (firstPacket) + { + foreach(Color::Color color, m_eatingorder) + m_updatepacket.add_eating_order(color); + } + QSharedPointer data = Util::createPacket(m_updatepacket); QMutableMapIterator i(m_clientConnections); while(i.hasNext()) @@ -349,53 +361,125 @@ void Server::botCalculate(Actor *actor) QPoint actorpos = CoordToMapPosition(actor->pos().toPoint()); /* first make list of possible directions based on current actor position */ - QList directions; + QHash directions; if (actorpos.x() > 0) - directions.append(Actor::Left); + directions.insert(Actor::Left, 0); if (actorpos.x() < visualMap.size() - 1) - directions.append(Actor::Right); + directions.insert(Actor::Right, 0); if (actorpos.y() > 0) - directions.append(Actor::Up); + directions.insert(Actor::Up, 0); if (actorpos.y() < visualMap[actorpos.x()].size() - 1) - directions.append(Actor::Down); + directions.insert(Actor::Down, 0); - /* copy of directions: used if there's no good direction based on rules */ - QList directions2(directions); - - QMutableListIterator i(directions); + /* check neighbours for blocks first */ + QMutableHashIterator i(directions); while(i.hasNext()) { i.next(); - Actor::Movement direction = i.value(); - QPoint pos = actorpos + Actor::movementToPoint(direction); - if (pos.x() < 0 || pos.x() >= visualMap.size()) + QPoint newpos = actorpos + Actor::movementToPoint(i.key()); + if (newpos.x() < 0 || newpos.x() >= visualMap.size()) continue; - if (pos.y() < 0 || pos.y() >= visualMap[pos.x()].size()) + if (newpos.y() < 0 || newpos.y() >= visualMap[newpos.x()].size()) continue; - GameEntity *item = visualMap[pos.x()][pos.y()]; + GameEntity *item = visualMap[newpos.x()][newpos.y()]; /* check if neighbour is a block */ Block *block = qgraphicsitem_cast(item); if (block != NULL && block->color() != actor->color()) { i.remove(); - directions2.removeAll(direction); continue; } + } + + /* we're enclosed by blocks */ + if (directions.empty()) + return; + + /* determine if other actors are in range to afraid/hunt*/ + int mindistance = Constants::AI::player_minimum_distance; + QList pos_afraid; + QList pos_hunt; + foreach (Actor *other, m_actors) + { + if (actor == other) + continue; + + QList *ptr = NULL; + if (other->canEat(actor, m_eatingorder)) + ptr = &pos_afraid; + else if (actor->canEat(other, m_eatingorder)) + ptr = &pos_hunt; + if (ptr == NULL) + continue; + + QPoint otherpos = CoordToMapPosition(other->pos().toPoint()); + QPoint distance = actorpos - otherpos; + if (distance.manhattanLength() < mindistance) + ptr->append(otherpos); + } - /* if neighbour is not a point */ + /* check new directions and change direction-weight */ + i = directions; + while(i.hasNext()) + { + i.next(); + QPoint newpos = actorpos + Actor::movementToPoint(i.key()); + + /* check for new positions in afraid list */ + foreach(QPoint otherpos, pos_afraid) + { + int olddistance = (actorpos - otherpos).manhattanLength(); + int newdistance = (newpos - otherpos).manhattanLength(); + if (newdistance >= olddistance) + i.setValue(i.value() += Constants::AI::weight_afraid); + } + + /* check for new positions in hunt list */ + foreach(QPoint otherpos, pos_hunt) + { + int olddistance = (actorpos - otherpos).manhattanLength(); + + int newdistance = (newpos - otherpos).manhattanLength(); + if (newdistance <= olddistance) + i.setValue(i.value() += Constants::AI::weight_hunt); + } + + /* check for bonuspoint */ + GameEntity *item = visualMap[newpos.x()][newpos.y()]; BonusPoint *bpoint = qgraphicsitem_cast(item); + if (bpoint != NULL) + i.setValue(i.value() + Constants::AI::weight_bonus_point); + + /* check for normal point */ Point *point = qgraphicsitem_cast(item); - if (bpoint == NULL && point == NULL) - { + if (point != NULL) + i.setValue(i.value() + Constants::AI::weight_point); + } + + /* sort directions */ + QList weightlist = directions.values(); + qSort(weightlist.begin(), weightlist.end(), qGreater()); + + /* remove directions with lesser weight */ + unsigned int max = weightlist.at(0); + i = directions; + while(i.hasNext()) + { + i.next(); + if (i.value() < max) i.remove(); - continue; - } } - QList *ptr = (!directions.empty()) ? &directions : &directions2; - int rand = (int) (ptr->size() * (qrand() / (RAND_MAX + 1.0))); - m_actorMovements[actor->color()] = ptr->at(rand); + QList list = directions.keys(); + + /* default is no direction change */ + if (list.contains(actor->direction())) + return; + + /* random direction */ + int rand = (int) (list.size() * (qrand() / (RAND_MAX + 1.0))); + m_actorMovements[actor->color()] = list.at(rand); } void Server::keyPressUpdate() @@ -504,6 +588,11 @@ bool Server::parseCommandline() return false; } m_maxplayers = maxplayers; + if (m_maxplayers == 2) + { + qCritical() << "2 player game is not supported (who wins if a player gets eaten?)"; + return false; + } } if (opt.getValue("bots") != NULL) diff --git a/pacman-c++/server.h b/pacman-c++/server.h index 8d40f27..826c701 100644 --- a/pacman-c++/server.h +++ b/pacman-c++/server.h @@ -32,7 +32,7 @@ protected: Transmission::map_t calculateUpdates(); /* update client maps */ - void sendUpdate(Transmission::map_t map); + void sendUpdate(Transmission::map_t map, bool firstPacket = false); QPoint addRandomPoint(Transmission::map_t map, Transmission::field_t type = Transmission::bonuspoint); void colorizeBlocks(Transmission::map_t map); -- cgit v1.2.3