Specike游戏的Minimax算法

时间:2018-03-18 00:44:04

标签: c++ oop minimax

我正在为c ++中名为Specker的游戏创建一个minimax播放器。

规则很简单:

  

有p个玩家(0到p - 1)和n个堆(0到n - 1)

     

从玩家0开始,每个玩家需要k>来自堆x的0个硬币并且在堆y上放置m个硬币(0 <= m      

获胜玩家是当所有堆中的所有硬币都被移除时播放的玩家

所以我创造了游戏和一些玩家类(GreedyPlayer,SpartanPlayer等),但他们都会对他们的行为有所预测。他们不是聪明的

所以我创建了一个根据minimax(pt18a038)播放的播放器代码编译得很好,但程序在执行时停止响应。 聪明的球员类:

class pt18a038 : public Player {
private:
    string player_type;

public:
    pt18a038(const string &n) : Player(n) {
        player_type = "Asder aka theRunner";
    }

    virtual const string &getType() const override {
        return player_type;
    }

    virtual Move play(const State &s) override {
        int source_heap = 0;
        int target_heap = 0;
        int source_coins = 0;
        int target_coins = 0;
        int sum = 0;

        for (source_heap = 0; source_heap < s.getHeaps(); source_heap++) {
            for (source_coins = 1; source_coins <= s.getCoins(source_heap); source_coins++) {
                for (target_heap = 0; target_heap < s.getHeaps(); target_heap++) {
                    for (target_coins = 0; target_coins <= source_coins; target_coins++) {
                        Move m(source_heap, source_coins, target_heap, target_coins);
                        sum = minimax(s, 3, 0, m);
                        cout << "Play:" << source_heap << "," << source_coins << "," << target_heap << ","
                             << target_coins << ":" << sum << endl;
                    }
                }
            }
        }
        cout << sum << endl;

        // ///////////// for debbuging only until minimax is working...
        source_heap = 0;
        source_coins = 0;
        for (int i = 0; i < s.getHeaps(); i++) {
            if (s.getCoins(i) > source_coins) {
                source_heap = i;
                source_coins = s.getCoins(i);
            }
        }
        Move SpartanObject(source_heap, 1, 0, 0);
        return SpartanObject;
        // /////////////

    }

    static int minimax(State s, const int &players, int depth, const Move move) {


        if (s.winning()) {
            cout << "game end!" << endl;
            return 1000;
            if (depth % players == 0) return 1000; //Maximazing player
            else return -1000; //Minimazing player
        }
        if (depth > 4) {
            //cout<<"optimazing"<<endl;
            return 0;
        }
        //cout << s << endl;
        s.next(move);


        int source_heap = 0;
        int target_heap = 0;
        int source_coins = 0;
        int target_coins = 0;
        int max = -100000;
        int min = 100000;
        int result;

        for (source_heap = 0; source_heap < s.getHeaps(); source_heap++) {
            for (source_coins = 1; source_coins <= s.getCoins(source_heap); source_coins++) {
                for (target_heap = 0; target_heap < s.getHeaps(); target_heap++) {
                    for (target_coins = 0; target_coins <= source_coins; target_coins++) {
                        //cout << "Move:" << source_heap << "," << source_coins << "," << target_heap << ","<< target_coins << endl;
                        Move m(source_heap, source_coins, target_heap, target_coins);
                        result = minimax(s, players, depth + 1, m);
                        if (depth % players == 0) {
                            max = result ? (result > max) : result;
                        } else {
                            min = result ? (result < min) : result;
                        }

                    }
                }
            }
        }

        return max ? (depth % players == 0) : min;

    }

};

这是我的游戏剩余部分的代码(经过测试并且工作正常)

#include <iostream>
#include <stdexcept>

using namespace std;

class Move {
private:
    int source_heap, source_coins, target_heap, target_coins;

public:
    Move(int sh, int sc, int th, int tc) {
        source_heap = sh;
        source_coins = sc;
        target_heap = th;
        target_coins = tc;
    }

    int getSource() const {
        return source_heap;
    }
    int getSourceCoins() const {
        return source_coins;
    }
    int getTarget() const {
        return target_heap;
    }
    int getTargetCoins() const {
        return target_coins;
    }

    // Let's do some operator overloading
    friend ostream &operator<<(ostream &out, const Move &move) {
        if (move.getTargetCoins()) {
            out << "takes " << move.getSourceCoins() << " coins from heap "
                << move.getSource() << " and puts " << move.getTargetCoins()
                << " coins to heap " << move.getTarget();

        } else {
            out << "takes " << move.getSourceCoins() << " coins from heap "
                << move.getSource() << " and puts nothing";
        }
    }
};

class State {
    // State with h heaps, where the i-th heap starts with c[i] coins.
private:
    int heaps, *heap_coins;

public:
    State(int h, const int c[]) {
        heaps = h;
        heap_coins = new int[heaps];
        for (int i = 0; i < heaps; i++)
            heap_coins[i] = c[i];
    }

    ~State() {
        delete[] heap_coins;
        return;
    }

    int getCoins(int h) const throw(logic_error) {
        if (h < 0 || h > heaps) {
            throw logic_error(
                "Invalid heap number, enter a number between 1 and heaps!");
            return 1;
        } else {
            return heap_coins[h];
        }
    }
    void next(const Move &move) throw(logic_error) {
        if ((move.getSource() < 0) || (move.getSource() > heaps) ||
            (move.getTarget() < 0) || (move.getTarget() > heaps)) {
            throw logic_error("Invalid Heap!");
            return;
        } else if (
            (move.getSourceCoins() < 1) || (move.getTargetCoins() < 0) ||
            (move.getSourceCoins() <= move.getTargetCoins()) ||
            (move.getSourceCoins() > getCoins(move.getSource()))) {
            throw logic_error("Invalid Coin number!");
        } else {
            heap_coins[move.getSource()] -= move.getSourceCoins();
            heap_coins[move.getTarget()] += move.getTargetCoins();
        }
    }

    bool winning() const {
        int s = 0;
        for (int i = 0; i < heaps; i++)
            s += getCoins(i);
        return not s; // yeah i know how booleans work :P
    }

    int getHeaps() const {
        return heaps;
    }

    friend ostream &operator<<(ostream &out, const State &state) {
        for (int i = 0; i < state.getHeaps(); i++) {
            out << state.heap_coins[i];
            if (i != state.getHeaps() - 1)
                out << ", ";
        }
        return out;
    }
};

class Player {
public:
    Player(const string &n);
    virtual ~Player();

    virtual const string &getType() const = 0;
    virtual Move play(const State &s) = 0;

    friend ostream &operator<<(ostream &out, const Player &player);

protected:
    string player_name;
};

class GreedyPlayer : public Player {
private:
    string player_type;

public:
    GreedyPlayer(const string &n) : Player(n) {
        player_type = "Greedy";
    }
    virtual const string &getType() const override {
        return player_type;
    }
    virtual Move play(const State &s) override {
        int source_heap = 0;
        int source_coins = 0;
        for (int i = 0; i < s.getHeaps(); i++) {
            if (s.getCoins(i) > source_coins) {
                source_heap = i;
                source_coins = s.getCoins(i);
            }
        }
        Move GreedyObject(source_heap, source_coins, 0, 0);
        return GreedyObject;
    }
};

class SpartanPlayer : public Player {
public:
    SpartanPlayer(const string &n) : Player(n) {
        player_type = "Spartan";
    }
    virtual const string &getType() const override {
        return player_type;
    }

    virtual Move play(const State &s) override {
        int source_heap = 0;
        int source_coins = 0;
        for (int i = 0; i < s.getHeaps(); i++) {
            if (s.getCoins(i) > source_coins) {
                source_heap = i;
                source_coins = s.getCoins(i);
            }
        }
        Move SpartanObject(source_heap, 1, 0, 0);
        return SpartanObject;
    }

private:
    string player_type;
};

class SneakyPlayer : public Player {
public:
    SneakyPlayer(const string &n) : Player(n) {
        player_type = "Sneaky";
    }
    virtual const string &getType() const override {
        return player_type;
    }

    virtual Move play(const State &s) override {
        int j = 0;
        while (s.getCoins(j) == 0) {
            j++;
        }
        int source_heap = j;
        int source_coins = s.getCoins(j);
        for (int i = j + 1; i < s.getHeaps(); i++) {
            if ((s.getCoins(i) < source_coins) && (s.getCoins(i) > 0)) {
                source_heap = i;
                source_coins = s.getCoins(i);
            }
        }
        Move SneakyObject(source_heap, source_coins, 0, 0);
        return SneakyObject;
    }

private:
    string player_type;
};

class RighteousPlayer : public Player {
public:
    RighteousPlayer(const string &n) : Player(n) {
        player_type = "Righteous";
    }
    virtual const string &getType() const override {
        return player_type;
    }

    virtual Move play(const State &s) override {
        int target_heap = 0;
        int source_heap = 0;
        int source_coins = s.getCoins(0);
        int target_coins = source_coins;

        for (int i = 1; i < s.getHeaps(); i++) {
            if (s.getCoins(i) > source_coins) {
                source_heap = i;
                source_coins = s.getCoins(i);
            } else if (s.getCoins(i) < target_coins) {
                target_heap = i;
                target_coins = s.getCoins(i);
            }
        }
        source_coins -= source_coins / 2;
        Move RighteousObject(
            source_heap, source_coins, target_heap, source_coins - 1);
        return RighteousObject;
    }

private:
    string player_type;
};

Player::Player(const string &n) {
    player_name = n;
}

Player::~Player() {
    player_name.clear();
}

ostream &operator<<(ostream &out, const Player &player) {
    out << player.getType() << " player " << player.player_name;
    return out;
}

class Game {
private:
    int game_heaps, game_players, current_heap, current_player;
    int *heap_coins;
    Player **players_list;

public:
    Game(int heaps, int players) {
        heap_coins= new int [heaps];
        game_heaps = heaps;
        game_players = players;
        current_heap = 0;
        current_player = 0;
        players_list = new Player*[players];
    }
    ~Game() {
        delete[] heap_coins;
        delete[] players_list;
    }
    void addHeap(int coins) throw(logic_error) {
        if (current_heap > game_heaps)
            throw logic_error("All heaps are full with coins!");
        else if (coins < 0)
            throw logic_error("Coins must be a positive number!"); 
        else {
                heap_coins[current_heap++] = coins;
            }
    }
    void addPlayer(Player *player) throw(logic_error) {
        if (current_player > game_players)
            throw logic_error("All players are added!");
        else {
            players_list[current_player++] = player;
        }
    }
    void play(ostream &out) throw(logic_error) {
        if ((current_player != game_players) && (current_heap != game_heaps)) {
            throw logic_error("Have you added all heaps and players?");
        } else {
            int i = 0;
            State currentState(game_heaps, heap_coins);
            while (!currentState.winning()) {
                out << "State: " << currentState << endl;
                out << *players_list[i % game_players] << " "
                    << players_list[i % game_players]->play(currentState) << endl;
                currentState.next(
                    players_list[i % game_players]->play(currentState));

                i++;
            }
            out << "State: " << currentState << endl;
            i--;
            out << *players_list[i % game_players] << " wins" << endl;
        }
    }
};


int main() {
Game specker(6, 5);
    specker.addHeap(10);
    specker.addHeap(20);
    specker.addHeap(17);
    specker.addHeap(17);
    specker.addHeap(17);
    specker.addHeap(17);

    specker.addPlayer(new GreedyPlayer("Alan"));
    specker.addPlayer(new SneakyPlayer("Tom"));
    specker.addPlayer(new SpartanPlayer("Mary"));
    specker.addPlayer(new RighteousPlayer("Robin"));

    specker.addPlayer(new pt18a038("Stavros"));
    specker.play(cout);

}

更新

1 个答案:

答案 0 :(得分:1)

我在你的minimax程序中看到了一些问题,其中一些是严肃的:

1)使用Alpha Beta修剪来减小搜索树的大小。

2)在minimax递归调用中没有适当的边界条件(如果深度> 5返回得分或其他东西)(有关详细信息,请参阅我的代码片段),CPU可能会在调用时挂起。

3)你的叶节点评估很弱,所以尽管使用了极小极大算法,评估的移动也不太可能是智能的。

4)为了提高搜索速度,您可以仅在顶级分支使用多线程。

5)如果叶子节点在最大化时给出获胜动作,则可以通过返回高分跳过进一步的评估。

6)一旦您的代码正常运行,请在代码审核中使用&#39; ai&#39;标签,而不是在SO,进行更详细的分析。

int EvaluateLeafNode(State s, const int &players)
{
  //TODO analyze here
  return score;
}

int minimax(State s, const int &players, int depth , const Move move, int alpha, int beta)
{
   if( depth >= 5) return EvaluateLeafNode(s,players);    // this was missing in your code

   //don't analyze here 

   //rest of minimax recursive code
}