// subscript (operator[])
void test_cluster_subscript(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Subscript ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("cluster with one point");

        {
            Cluster c;
            Point p(10);
            p[5] = 3.14;

            c.add(p);
            Point p1 = c[0];

            pass = p1[5] == 3.14;

            ec.result(pass);
        }

        ec.DESC("cluster with several point");

        {
            Cluster c;
            for (int i = 0; i < 10; i ++) {
                Point p(10);
                p[5] = 3.14;
                c.add(p);
            }

            pass = true;
            for (int i = 0; i < 10; i ++) {
                Point p1 = c[i];
                pass = pass && (p1[5] == 3.14);
            }

            ec.result(pass);
        }
    }
}
// operator=
void test_point_assignment(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Assign ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("simple assignment");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 44.56 * i * i + 23.45 * i + 12.34;

            Point p2 = p1;

            pass = true;
            for (int i = 0; i < 50; i++)
                pass = pass && (p1[i] == p2[i]);
            ec.result(pass);
        }

        ec.DESC("chained assignment");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 44.56 * i * i + 23.45 * i + 12.34;

            Point p2(50), p3(50), p4(50), p5(50);

            p2 = p3 = p4 = p5 = p1;

            pass = true;
            for (int i = 0; i < 50; i++)
                pass = pass && (p1[i] == p2[i]);
            ec.result(pass);
        }
    }
}
// Containment
void test_cluster_contain(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Containment ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("cluster with one point");

        {
            Point p(10);
            p[0] = p[2] = p[4] = p[8] = 6.705;
            Cluster c;
            c.add(p);

            pass = c.contains(p);

            ec.result(pass);
        }

        ec.DESC("cluster with several points");

        {
            Point p(10);
            p[0] = p[2] = p[4] = p[8] = 6.705;
            Cluster c;

            for (int i = 0; i < 10; i ++) {
                Point pp(10);
                for (int j = 0; j < 10; j ++) {
                    pp[i] = 3.4 + i * 2.1 + i * i;
                }
                c.add(pp);
            }
            c.add(p);

            pass = c.contains(p);

            ec.result(pass);
        }
    }
}
// Copy constructor
void test_point_copying(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Copy ---");

    for (int run = 0; run < numRuns; run ++) {

        ec.DESC("simple copy");

        {
            Point p1(50);

            for (int i = 0; i < 50; i ++)
                p1[i] = 44.56 * i * i + 23.45 * i + 12.34;

            Point p2(p1);

            pass = true;
            for (int i = 0; i < 50; i ++)
                pass = pass && (p1[i] == p2[i]);
            ec.result(pass);
        }

        ec.DESC("pass and return by value");

        {
            Point p1(50);

            for (int i = 0; i < 50; i ++)
                p1[i] = 44.56 * i * i + 23.45 * i + 12.34;

            Point p2 = point_in_and_out(p1);

            pass = true;
            for (int i = 0; i < 50; i ++)
                pass = pass && (p1[i] == p2[i]);
            ec.result(pass);
        }
    }
}
void test_piece_print(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Piece - Id-s, names, printing ---");

    for (int run = 0; run < numRuns; run ++) {

        ec.DESC("piece id-s, names, and printing");

        {
            Game g;                         // note: Game smoke test is needed first

            Position p0(0, 0);
            Simple s(g, p0, 10);

            Position p1(1, 0);
            Strategic t(g, p1, 20);

            Position p2(2, 2);
            Food f(g, p2, 5);

            Position p3(0, 2);
            Advantage a(g, p3, 3);

            std::stringstream ss;
            ss << s << ' ' << t << ' ' << f << ' ' << a;

            int id = 0;
            std::regex re("S[[:d:]]{1,}[ ]"); // ECMAScript, by default
            std::smatch m;
            std::string search_str(ss.str()); // convert string to lvalue
//            std::regex_search(ss.str(), m, re);
            std::regex_search(search_str, m, re);
            if (m.size() != 1) { // parse problem
                pass = false;
            } else {
                std::string matchStr(m[0]);
                std::regex r("[[:d:]]{1,}");
                std::regex_search(matchStr, m, r);
                id = stoi(m[0]);
                pass = true;
            }

            std::stringstream compare;
            compare << 'S' << id << " T" << (id+1) << " F" << (id+2) << " D" << (id+3);

            pass = pass && (ss.str() == compare.str());

            ec.result(pass);
        }
    }
}
// Copy constructor
void test_cluster_copying(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Copy ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("simple copy");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);
            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            Cluster c2(c1);

            pass = (c1 == c2);

            ec.result(pass);
        }

        ec.DESC("chained copy");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);
            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            Cluster c2(c1), c3(c2), c4(c3);

            pass = (c1 == c4);

            ec.result(pass);
        }
    }
}
// Smoketest: constructor, copy constructor, destructor
void test_cluster_smoketest(ErrorContext &ec) {
    bool pass;

    ec.DESC("--- Test - Cluster - Smoketest ---");

    ec.DESC("constructor, destructor");
    pass = true;
    for (int i = 0; i < 10; i ++) {

        Cluster c;
    }
    ec.result(pass);


    ec.DESC("size getter - implement if you haven't");
    pass = true;
    for (int i = 0; i < 10; i ++) {

        // Construct a Point
        // At the end of the block, destructor will be called
        Cluster c;

        pass = (c.getSize() == 0);
        if (!pass) break;
    }
    ec.result(pass);


    ec.DESC("copy constructor");
    pass = true;
    for (int i = 0; i < 10; i ++) {

        Cluster c1, c2(c1);
        pass = (c1 == c2);
        if (!pass) break;
        
    }
    
    ec.result(pass);
}
// id
void test_point_id(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Point ID ---");

    for (int run = 0; run < numRuns; run ++)
    {
        ec.DESC("get a point's id");

        {
            Point p(15);

            pass = (p.getId() >= 0);

            ec.result(pass);
        }

        ec.DESC("sequential id-s");

        {
            Point **points = new Point*[100];
            for (int i=0; i<100; i++)
                points[i] = new Point(15);

            pass = true;
            int firstId = points[0]->getId();
            for (int i=0; i<100; i++)
                pass = pass && (points[i]->getId() == (firstId + i));

            for (int i=0; i<100; i++)
                delete points[i];
            delete [] points;

            ec.result(pass);
        }
    }
}
// Smoketest: constructor, copy constructor, destructor, number of pieces
void test_game_smoketest(ErrorContext &ec) {
    bool pass;

    ec.DESC("--- Test - Game - Smoketest ---");

    ec.DESC("constructor, empty game, default grid size, destructor");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g;

        pass = (g.getWidth() == 3 &&
                g.getHeight() == 3 &&
                g.getNumPieces() == 0);
    }
    ec.result(pass);

    ec.DESC("constructor with dimensions");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g(4, 5);

        pass = (g.getWidth() == 4 &&
                g.getHeight() == 5 &&
                g.getNumPieces() == 0);
    }
    ec.result(pass);

    ec.DESC("insufficient dimensions (exception generated)");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        try {
            Game g(Game::MIN_WIDTH-1, 5);
            pass = false;
        } catch (InsufficientDimensionsEx &ex) {
            std::cerr << "Exception generated: " << ex << std::endl;
            pass = (ex.getName() == "InsufficientDimensionsEx");
        }
    }
    ec.result(pass);
}
// operator==, operator!=
void test_cluster_equality(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Equal ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("check operator== is not a dummy");

        {
            // The requirements don't provide for many other methods that
            // can be used for testing, so operator== is checked first
            Cluster c1, c2;
            c1.add(Point(100));

            pass = !(c1 == c2);

            ec.result(pass);
        }

        ec.DESC("check inequality");

        {
            // The requirements don't provide for many other methods that
            // can be used for testing, so operator== is checked first
            Cluster c1, c2;
            c1.add(Point(100));

            pass = (c1 != c2);

            ec.result(pass);
        }
    }
}
// operator>>, operator<<
void test_cluster_IO(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Stream IO ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("read from a file");

        {
            std::ifstream csv("points.csv");
            Cluster c;
            if (csv.is_open()) {
                csv >> c;
                csv.close();
            }
            pass = (c.getSize() == 4);

            ec.result(pass);
        }

        ec.DESC("read, write, and read again");

        {
            std::ifstream csv("points.csv");
            Cluster c;
            if (csv.is_open()) {
                csv >> c;
                csv.close();
            }
            pass = (c.getSize() == 4);

            // add a point
            c.add(Point(5));

            std::ofstream csv1("points1.csv", std::ofstream::out);

            csv1 << c;
            csv1.close();

            std::ifstream csv2("points1.csv");
            Cluster c2;
            if (csv2.is_open()) {
                csv2 >> c2;
                csv2.close();
            }
Exemple #12
0
void empty(ErrorContext &ec)
{
    ec.DESC("--- Empty matrices (0 by 0) ---");

    // If this test fails, it means that getrows returned a non-zero value
    // when called on an "empty" (0x0) matrix created with the default
    // constructor.
    ec.DESC("default constructor and getrows");

    // Construct an empty matrix using the default constructor.
    const Matrix a;

    // Make sure 'getrows' method returns zero for this matrix.
    ec.result(a.getrows() == 0);


    // Same as above, for getcols instead of getrows.
    ec.DESC("default constructor and getcols");
    ec.result(a.getcols() == 0);


    // If this test fails, it means that getrows returned a non-zero value
    // when called on an "empty" (0x0) matrix created with the 2-argument
    // constructor.
    ec.DESC("two-argument constructor and getrows");

    // Construct an empty matrix using the two-argument constructor.
    const Matrix b(0, 0);

    // Make sure 'getrows' method returns zero for this matrix.
    ec.result(b.getrows() == 0);

    // Same as above, for getcols instead of getrows.
    ec.DESC("two-argument constructor and getcols");
    ec.result(b.getcols() == 0);
}
// Smoketest: constructor, copy constructor, destructor, resources
void test_piece_smoketest(ErrorContext &ec) {
    bool pass;

    ec.DESC("--- Test - Unit - Smoketest ---");

    ec.DESC("constructor, destructor for leaf classes from Piece hierarchy");
    pass = true;
    for (int i = 0; i < 10; i++) {
        Game g;                         // note: Game smoke test is needed first

        Position p0(0, 0);
        Simple s(g, p0, 10);

        Position p1(1, 0);
        Strategic t(g, p1, 20);

        Position p2(2, 2);
        Food f(g, p2, 5);

        Position p3(0, 2);
        Advantage a(g, p3, 3);
    }
    ec.result(pass);
}
// operator<, operator<=, operator>, operator>=
// (pseudo-lexicographic comparison)
void test_point_comparison(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Compare ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("compare pseudo-lexicographic order");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = i;
                p2[i] = i + 1.0;
                p3[i] = i + 2.0;
            }

            pass = (p1 < p2) &&
                   (p2 < p3) &&
                   (p1 < p3);
            ec.result(pass);
        }

        ec.DESC("less than, one different value, leading");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }
            p2[1] = p1[1] + std::numeric_limits<double>::epsilon();
            p3[1] = p2[1] + std::numeric_limits<double>::epsilon();

            pass = (p1 < p2) &&
                   (p2 < p3) &&
                   (p1 < p3);
            ec.result(pass);
        }

        ec.DESC("less than, one different value, middle");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }
            p2[30] = p1[30] + 0.00000001;
            p3[30] = p2[30] + 0.00000001;

            pass = (p1 < p2) &&
                   (p2 < p3) &&
                   (p1 < p3);
            ec.result(pass);
        }

        ec.DESC("less than, one different value, trailing");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }
            p2[49] = p1[49] + 0.00000001;
            p3[49] = p2[49] + 0.00000001;

            pass = (p1 < p2) &&
                   (p2 < p3) &&
                   (p1 < p3);
            ec.result(pass);
        }

        ec.DESC("less than or equal, equal values");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }

            pass = (p1 <= p2) &&
                   (p2 <= p3) &&
                   (p1 <= p3);
            ec.result(pass);
        }

        ec.DESC("less than or equal, one different value, trailing");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }
            p2[49] = p1[49] + 0.00000001;
            p3[49] = p2[49] + 0.00000001;

            pass = (p1 <= p2) &&
                   (p2 <= p3) &&
                   (p1 <= p3);
            ec.result(pass);
        }

        ec.DESC("more than or equal, equal values");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }

            pass = (p1 >= p2) &&
                   (p2 >= p3) &&
                   (p1 >= p3);
            ec.result(pass);
        }

        ec.DESC("more than or equal, one different value, middle");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }

            p2[30] = p3[30] + 0.00000001;
            p1[30] = p2[30] + 0.00000001;

            pass = (p1 >= p2) &&
                   (p2 >= p3) &&
                   (p1 >= p3);
            ec.result(pass);
        }

        ec.DESC("more than, one different value, middle");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i ++) {
                p1[i] = p2[i] = p3[i] = i;
            }

            p2[30] = p3[30] + 0.00000001;
            p1[30] = p2[30] + 0.00000001;

            pass = (p1 > p2) &&
                   (p2 > p3) &&
                   (p1 > p3);
            ec.result(pass);
        }
    }
}
// Randomization of motion
void test_game_randomization(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Game - Randomization ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("position randomizer");

        {
            std::vector<int> positions;
            for (int i = 0; i < 4; i++) positions.push_back(i);
            for (int i = 5; i < 9; i++) positions.push_back(i);
            Position pos;
            unsigned counts[9];
            for (auto &c : counts) c = 0;
            for (int i = 0; i < 1000; i++) {
                pos = Game::randomPosition(positions);
                ++ counts[pos.x * 3 + pos.y];
            }

            pass = counts[0] > 100 &&
                    counts[1] > 100 &&
                    counts[2] > 100 &&
                    counts[3] > 100 &&
                    counts[4] == 0 &&
                    counts[5] > 100 &&
                    counts[6] > 100 &&
                    counts[7] > 100 &&
                    counts[8] > 100;

            if (! pass) for (auto c : counts) std::cout << c << ' ';


            ec.result(pass);
        }

        ec.DESC("position randomizer, empty vector (exception generated)");

        {
            std::vector<int> positions;

            try {
                Position pos = Game::randomPosition(positions);
                pass = false;
            } catch (PosVectorEmptyEx &ex) {
                std::cerr << "Exception generated: " << ex << std::endl;
                pass = (ex.getName() == "PosVectorEmptyEx");
            }

            ec.result(pass);
        }

        ec.DESC("random walk of a Simple agent");

        {
            Game g(101, 101);
            Position pos(50, 50);
            g.addSimple(Position(pos), 1000 * Game::STARTING_AGENT_ENERGY);
            const Piece *piece = g.getPiece(pos.x, pos.y);

            unsigned actionCounts[ActionType::STAY + 1];
            for (auto &a : actionCounts) a = 0;
            Position oldPos = pos;
            for (int i = 0; i < 1000; i ++) {
                g.round();
                pos = piece->getPosition();
                assert(pos.x != oldPos.x || pos.y != oldPos.y);
                assert(piece->isViable());
                ActionType actionType = g.reachSurroundings(oldPos, pos);
                ++ actionCounts[actionType];
                oldPos = pos;
            }

            pass = actionCounts[ActionType::NE] > 100 &&
                    actionCounts[ActionType::NW] > 100 &&
                    actionCounts[ActionType::N] > 100 &&
                    actionCounts[ActionType::W] > 100 &&
                    actionCounts[ActionType::E] > 100 &&
                    actionCounts[ActionType::SW] > 100 &&
                    actionCounts[ActionType::SE] > 100 &&
                    actionCounts[ActionType::S] > 100 &&
                    actionCounts[ActionType::STAY] == 0;

            if (! pass) {
                std::cout << std::endl;
                for (auto c : actionCounts) std::cout << c << ' ';
            }

            ec.result(pass);
        }

    }
}
// Playing and termination of a game
void test_game_play(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Game - Play ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("3x3 grid, manual, game w/o resources terminates immediately");

        {
            Game g; // manual = true, by default

            g.play(); // verbose = false, by default

            pass = (g.getNumResources() == 0);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 simple, 1 resource");

        {
            Game g; // manual = true, by default
            g.addSimple(1, 1);
            g.addFood(2, 2);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumAgents() == 1);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 simple, 2 resources");

        {
            Game g; // manual = true, by default
            g.addSimple(1, 1);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumAgents() == 1);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 simple, 3 resources");

        {
            Game g; // manual = true, by default
            g.addSimple(0, 0);
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumAgents() == 1);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 2 simple, 3 resources");

        {
            Game g; // manual = true, by default
            g.addSimple(0, 0);
            g.addSimple(0, 1);
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false);

            pass = (g.getNumResources() == 0) &&
                   (g.getNumAgents() == 2);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 3 simple, 3 resources");

        {
            Game g; // manual = true, by default
            g.addSimple(0, 0);
            g.addSimple(0, 1);
            g.addSimple(1, 1);
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumAgents() == 3);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 default strategic, 1 simple, 3 resources");

        {
            Game g; // manual = true, by default
            g.addSimple(0, 0);
            g.addStrategic(0, 1);
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumStrategic() == 1) &&
                   (g.getNumSimple() == 1);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 aggressive strategic, 3 resources");

        {
            Game g; // manual = true, by default
            g.addStrategic(0, 1, new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumStrategic() == 1);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 aggressive strategic, 1 far simple, 3 res");

        {
            Game g; // manual = true, by default
            // In this configuration, the Simple and Strategic are far from each other
            // and they might or might not get close to each other before the
            // Resources run out
            g.addStrategic(0, 1, new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));
            g.addSimple(2, 1);
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumStrategic() == 1 ) &&
                   (g.getNumSimple() <= 1); // randomized game play

            ec.result(pass);
        }


        ec.DESC("3x3 grid, manual, 1 aggressive strategic, 1 near simple, 3 res");

        {
            Game g; // manual = true, by default

            // In this configuration, the Simple gets an Advantage and when the
            // aggressive Strategic challenges it, the Strategic loses and disappears
            g.addSimple(0, 0);
            g.addStrategic(0, 1, new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));
            g.addFood(0, 2);
            g.addFood(2, 2);
            g.addAdvantage(1, 0);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   (g.getNumSimple() == 1) &&
                   (g.getNumStrategic() == 0);

            ec.result(pass);
        }

        ec.DESC("3x3 grid, manual, 1 def, 1 aggr, 1 simple, 2 resources");

        {
            Game g; // manual = true, by default
            // In this situation, the default flees, the aggressive attacks either
            // the default or the Simple, and the dies with it, leaving the other
            // one to win
            g.addStrategic(0, 0);
            g.addSimple(1, 0);
            g.addStrategic(0, 1, new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));
            g.addFood(0, 2);
            g.addFood(2, 2);

            g.play(false); // verbose = false, by default

            pass = (g.getNumResources() == 0) &&
                   ((g.getNumStrategic() == 1 && g.getNumSimple() == 0) ||
                    (g.getNumStrategic() == 0 && g.getNumSimple() == 1));

            ec.result(pass);
        }
    }
}
// Surroundings (vector of enums of type PieceType) smoke test
void test_surroundings_smoketest(ErrorContext &ec) {
    bool pass;

    ec.DESC("--- Test - Surroundings - Smoketest ---");

    ec.DESC("printing PieceTypes");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        std::stringstream ss;
        ss << PieceType::SIMPLE << ' '
        << PieceType::STRATEGIC << ' '
        << PieceType::FOOD << ' '
        << PieceType::ADVANTAGE << ' '
        << PieceType::INACCESSIBLE << ' '
        << PieceType::SELF << ' '
        << PieceType::EMPTY;

        pass = (ss.str() == "0 1 2 3 4 5 6");
//        if (! pass) std::cout << ss.str() << std::endl;
    }
    ec.result(pass);

    ec.DESC("3x3 grid, manual, surroundings of agents");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g;                     // note: Game smoke test required first

        Position p0(0, 0);
        g.addSimple(p0);

        Position p1(1, 0);
        g.addStrategic(p1);

        Position p2(2, 2);
        g.addFood(p2);

        Position p3(0, 2);
        g.addAdvantage(p3);

        // The surroundings of the simple agent
        Surroundings surr = g.getSurroundings(p0);
        std::stringstream ss0;
        for (int i = 0; i < 9; i++)
            ss0 << surr.array[i] << ' ';

        pass = (ss0.str() == "4 4 4 4 5 6 4 1 6 ");
        if (! pass) std::cout << ss0.str() << std::endl;

        // The surroundings of the strategic agent
        surr = g.getSurroundings(p1);
        std::stringstream ss1;
        for (int i = 0; i < 9; i++)
            ss1 << surr.array[i] << ' ';

        pass = pass && (ss1.str() == "4 0 6 4 5 6 4 6 6 ");
        if (! pass) std::cout << ss1.str() << std::endl;

    }
    ec.result(pass);

    ec.DESC("4x5 grid, manual, surroundings of agents");
    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g(4, 5);                     // note: Game smoke test required first

//        std::cout << g << std::endl;

        g.addSimple(0, 1); Position p0(0, 1);
        g.addAdvantage(1, 0);
        g.addAdvantage(1, 1);
        g.addFood(1, 3);
        g.addStrategic(2, 2); Position p1(2, 2);
        g.addFood(3, 1);
        g.addSimple(3, 2); Position p2(3, 2);
        g.addStrategic(4, 3); Position p3(4, 3);


        // The surroundings of the simple agent at p0
        Surroundings surr = g.getSurroundings(p0);
        std::stringstream ss0;
        for (int i = 0; i < 9; i++) ss0 << surr.array[i] << ' ';

        pass = (ss0.str() == "4 4 4 6 5 6 3 3 6 ");
        if (! pass) std::cout << ss0.str() << std::endl;


        // The surroundings of the strategic agent at p1
        surr = g.getSurroundings(p1);
        std::stringstream ss1;
        for (int i = 0; i < 9; i++)
            ss1 << surr.array[i] << ' ';

        pass = pass && (ss1.str() == "3 6 2 6 5 6 2 0 6 ");
        if (! pass) std::cout << ss1.str() << std::endl;


        // The surroundings of the simple agent at p2
        surr = g.getSurroundings(p2);
        std::stringstream ss2;
        for (int i = 0; i < 9; i++)
            ss2 << surr.array[i] << ' ';

        pass = pass && (ss2.str() == "6 1 6 2 5 6 6 6 1 ");
        if (! pass) std::cout << ss2.str() << std::endl;


        // The surroundings of the strategic agent at p3
        surr = g.getSurroundings(p3);
        std::stringstream ss3;
        for (int i = 0; i < 9; i++)
            ss3 << surr.array[i] << ' ';

        pass = pass && (ss3.str() == "0 6 4 6 5 4 4 4 4 ");
        if (! pass) std::cout << ss3.str() << std::endl;
    }
    ec.result(pass);
}
// distanceTo
void test_point_distance(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Distance ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("same point");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 2.4 * i * i + 1.3 * i + 6.7;

            Point p2(p1);

            pass = (p1.distanceTo(p2) == 0);
            ec.result(pass);
        }

        ec.DESC("5 units away");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = i;

            Point p2(p1);
            p2[1] += 5;

            pass = (p1.distanceTo(p2) == 5);
            if (!pass) std::cout << p1.distanceTo(p2) << " ";
            ec.result(pass);
        }

        // Integer sequence A180442

        ec.DESC("distance 1612 from origin");

        {
            Point p1(169); // 198 - 29

            unsigned int start = 30;
            for (int i = 0; i < 169; i++) {
                p1[i] = start;
                start++;
            }

            Point origin(169); // relies on initialization to zeros

            pass = (p1.distanceTo(origin) == 1612);
            if (!pass) std::cout << p1.distanceTo(origin) << " ";
            ec.result(pass);
        }
    }
}
// Taking turns
void test_piece_turntaking(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Piece - Taking turns ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("3x3, manual, simple agent going for a resource");

        {
            Game g(3, 3);

            // populate the game environment
            g.addFood(0, 0); g.addAdvantage(2, 1);

            // create a Simple, passing it the game and a position
            Simple s(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::NW) || (action == ActionType::S);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, simple agent moving to an empty position");

        {
            Game g(3, 3);

            // populate the game environment
            g.addStrategic(0, 1); g.addSimple(1, 1);

            // create a Simple, passing it the game and a position
            Simple s(g, Position(0, 2), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(0, 2));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::S);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, simple agent staying in place, not attacking");

        {
            Game g(3, 3);

            // populate the game environment
            g.addStrategic(0, 1); g.addSimple(1, 1); g.addSimple(1, 2);

            // create a Simple, passing it the game and a position
            Simple s(g, Position(0, 2), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(0, 2));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::STAY);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, resources don't move");

        {
            Game g(3, 3);

            Food f(g, Position(0, 0), Game::STARTING_RESOURCE_CAPACITY);
            Advantage a(g, Position(2, 1), Game::STARTING_RESOURCE_CAPACITY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece[2] = { &f, &a };

            // get the Surroundings for a Piece's position
            Surroundings surr[2] =
                    { g.getSurroundings(Position(0, 0)), g.getSurroundings(Position(2, 1)) };

            // call takeTurn on the Piece pointer
            ActionType actions[2] =
                    { piece[0]->takeTurn(surr[0]), piece[1]->takeTurn(surr[1]) };

            pass = (actions[0] == ActionType::STAY) &&
                    (actions[1] == ActionType::STAY);

            ec.result(pass);
        }

        ec.DESC("3x3, manual, default strategic agent choosing an advantage");

        {
            Game g(3, 3);

            // populate the game environment
            g.addFood(0, 0); g.addAdvantage(2, 1);

            // create a default Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::S);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, hemmed in default strategic challenging a simple");

        {
            Game g(3, 3);

            // populate the game environment
            g.addSimple(0, 0); g.addSimple(0, 1); g.addSimple(0, 2);
            g.addStrategic(1, 0); g.addStrategic(1, 2);
            g.addSimple(2, 0); g.addSimple(2, 1); g.addSimple(2, 2);

            // create a default Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::NW) ||
                   (action == ActionType::N)  ||
                   (action == ActionType::NE) ||
                   (action == ActionType::SW) ||
                   (action == ActionType::S)  ||
                   (action == ActionType::SE);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, hemmed in default strategic staying put");

        {
            Game g(3, 3);

            // populate the game environment
            g.addStrategic(0, 0); g.addStrategic(0, 1); g.addStrategic(0, 2);
            g.addStrategic(1, 0); g.addStrategic(1, 2);
            g.addStrategic(2, 0); g.addStrategic(2, 1); g.addStrategic(2, 2);

            // create a default Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::STAY);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, hemmed in default strategic fleeing");

        {
            Game g(3, 3);

            // populate the game environment
            g.addStrategic(0, 0); g.addStrategic(0, 1); g.addStrategic(0, 2);
            g.addStrategic(1, 0);
            g.addStrategic(2, 0); g.addStrategic(2, 1); g.addStrategic(2, 2);

            // create a default Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::E);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, hemmed in aggressive strategic attacking");

        {
            Game g(3, 3);

            // populate the game environment
            g.addStrategic(0, 0); g.addStrategic(0, 1); g.addStrategic(0, 2);
            g.addStrategic(1, 0);
            g.addStrategic(2, 0); g.addStrategic(2, 1); g.addStrategic(2, 2);

            // create an aggressive Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1),
                        Game::STARTING_AGENT_ENERGY,
                        new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action != ActionType::E);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, strong aggressive strategic prefers to attack");

        {
            Game g(3, 3);

            // populate the game environment
            g.addFood(0, 0); g.addAdvantage(0, 2);
            g.addStrategic(1, 0);
            g.addFood(2, 1); g.addSimple(2, 2);

            // create an aggressive Strategic, passing it the game and a position
            Strategic s(g, Position(1, 1),
                        Game::STARTING_AGENT_ENERGY,
                        new AggressiveAgentStrategy(Game::STARTING_AGENT_ENERGY));

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::W) || (action == ActionType::SE);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }

        ec.DESC("3x3, manual, weak aggressive strategic goes for an advantage");

        {
            Game g(3, 3);

            // populate the game environment
            g.addFood(0, 0); g.addAdvantage(0, 2);
            g.addStrategic(1, 0);
            g.addFood(2, 1); g.addSimple(2, 2);

            // create an aggressive Strategic, passing it the game and a position
            double energy = Game::STARTING_AGENT_ENERGY / 2; // weaken the agent
            Strategic s(g, Position(1, 1), energy, new AggressiveAgentStrategy(energy));

            // create an upcast pointer to the agent for polymorphic turn taking
            Piece *piece = &s;

            // get the Surroundings for the agent's position from the Game
            Surroundings surr = g.getSurroundings(Position(1, 1));

            // call takeTurn on the Piece pointer to the agent
            ActionType action = piece->takeTurn(surr);

            // if there is a resource, it should ask to there
            // and so on...
            pass = (action == ActionType::NE);
            if (! pass) std::cout << action << std::endl;

            ec.result(pass);
        }
    }
}
// populate the game grid
void test_game_populate(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Game - Populate ---"); // note: piece smoke test needed first

    for (int run = 0; run < numRuns; run ++) {
        ec.DESC("3x3 grid, manual population");

        {
            Game g;

            g.addSimple(0, 0);
            g.addStrategic(1, 1);
            g.addFood(0, 2);
            g.addFood(2, 1);
            g.addAdvantage(2, 2);

            pass = (g.getNumPieces() == 5) &&
                   (g.getNumAgents() == 2) &&
                   (g.getNumResources() == 3);

            ec.result(pass);
        }

        ec.DESC("4x5 grid, manual population");

        {
            Game g(4, 5);

            g.addSimple(0, 0);
            g.addStrategic(1, 1);
            g.addFood(0, 2);
            g.addFood(2, 1);
            g.addAdvantage(2, 2);
            g.addSimple(4, 3);
            g.addAdvantage(2, 3);

            try {
                g.addFood(4, 3);
                pass = false;
            } catch (PositionNonemptyEx &ex) {
                std::cerr << "Exception generated: " << ex << std::endl;
                pass = (ex.getName() == "PositionNonemptyEx");
            }
            try {
                g.addStrategic(0, 2);
                pass = false;
            } catch (PositionNonemptyEx &ex) {
                std::cerr << "Exception generated: " << ex << std::endl;
                pass = (ex.getName() == "PositionNonemptyEx");
            }

            pass = pass &&
                          (g.getNumPieces() == 7) &&
                          (g.getNumAgents() == 3) &&
                          (g.getNumResources() == 4);

            ec.result(pass);
        }

        ec.DESC("4x5 grid, manual, out of bounds (exception generated)");

        {
            Game g(4, 5);

            g.addSimple(0, 0);
            g.addStrategic(1, 1);
            g.addFood(0, 2);

            try {
                g.addAdvantage(4, 5);
                pass = false;
            } catch (OutOfBoundsEx &ex) {
                std::cerr << "Exception generated: " << ex << std::endl;
                pass = pass && (ex.getName() == "OutOfBoundsEx");
            }

            try {
                Position pos(6, 10);
                g.addStrategic(pos);
                pass = false;
            } catch (OutOfBoundsEx &ex) {
                std::cerr << "Exception generated: " << ex << std::endl;
                pass = pass && (ex.getName() == "OutOfBoundsEx");
            }

            ec.result(pass);
        }

        ec.DESC("3x3 grid, auto population");

        {
            Game g(3, 3, false);

            pass = (g.getNumAgents() == 2) &&
                    (g.getNumResources() == 4);

            ec.result(pass);
        }

        ec.DESC("4x5 grid, auto population");

        {
            Game g(4, 5, false);

            pass = (g.getNumAgents() == 5) &&
                   (g.getNumResources() == 10);

            ec.result(pass);
        }

        ec.DESC("9x9 grid, auto population");

        {
            Game g(9, 9, false);

            pass = (g.getNumAgents() == 20) &&
                   (g.getNumResources() == 40);

            if (! pass) std::cout << g.getNumAgents() << ' '
                << g.getNumResources() << ' ' << std::endl << g << ' ';

            ec.result(pass);
        }
    }
}
// Action smoke test
void test_action_smoketest(ErrorContext &ec) {
    bool pass;

    ec.DESC("--- Test - Action - Smoketest ---");

    ec.DESC("printing ActionType");

    pass = true;
    for (int i = 0; i < 10; i ++) {
        std::stringstream ss;
        ss << ActionType::N << ' '
        << ActionType::NE << ' '
        << ActionType::NW << ' '
        << ActionType::E << ' '
        << ActionType::W << ' '
        << ActionType::SE << ' '
        << ActionType::SW << ' '
        << ActionType::S << ' '
        << ActionType::STAY;

        pass = (ss.str() == "0 1 2 3 4 5 6 7 8");
//        if (! pass) std::cout << ss.str() << std::endl;
    }
    ec.result(pass);

    ec.DESC("3x3, manual, agent in the middle");

    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g;

        // not actually necessary for the test
        g.addSimple(1, 1); Position pos(1, 1);

        pass = g.isLegal(ActionType::N, pos) &&
                g.isLegal(ActionType::NE, pos) &&
                g.isLegal(ActionType::NW, pos) &&
                g.isLegal(ActionType::E, pos) &&
                g.isLegal(ActionType::W, pos) &&
                g.isLegal(ActionType::SE, pos) &&
                g.isLegal(ActionType::SW, pos) &&
                g.isLegal(ActionType::S, pos) &&
                g.isLegal(ActionType::STAY, pos);
    }
    ec.result(pass);

    ec.DESC("7x6, manual, agent in the SW corner");

    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g(7, 6);

        // not actually necessary for the test
        g.addSimple(5, 6); Position pos(5, 6);

        pass = g.isLegal(ActionType::N, pos) &&
               (! g.isLegal(ActionType::NE, pos)) &&
               g.isLegal(ActionType::NW, pos) &&
               (! g.isLegal(ActionType::E, pos)) &&
               g.isLegal(ActionType::W, pos) &&
               (! g.isLegal(ActionType::SE, pos)) &&
               (! g.isLegal(ActionType::SW, pos)) &&
               (! g.isLegal(ActionType::S, pos)) &&
               g.isLegal(ActionType::STAY, pos);
    }
    ec.result(pass);

    ec.DESC("7x6, movement from position to position");

    pass = true;
    for (int i = 0; i < 10; i ++) {
        Game g(13, 9); // width - y, height - x

        g.addSimple(0, 12); Position ne(0, 12);

        Position p0 = (g.isLegal(ActionType::NE, ne)) ?
                      g.move(ne, ActionType::NE) : ne;

        pass = (p0.x == ne.x) && (p0.y == ne.y);

        p0 = (g.isLegal(ActionType::S, ne)) ?
             g.move(ne, ActionType::S) : ne;

        pass = pass && (p0.x == ne.x+1) && (p0.y == ne.y);

        p0 = (g.isLegal(ActionType::W, ne)) ?
             g.move(ne, ActionType::W) : ne;

        pass = pass && (p0.x == ne.x) && (p0.y == ne.y-1);

        p0 = (g.isLegal(ActionType::SE, ne)) ?
             g.move(ne, ActionType::SE) : ne;

        pass = pass && (p0.x == ne.x) && (p0.y == ne.y);
    }
    ec.result(pass);

    ec.DESC("7x6, action needed to reach one position from another");

    pass = true;
    for (int i = 0; i < 10; i ++) {

        Game g(5, 5);

        g.addSimple(1, 1); Position simpos(1, 1);
        g.addFood(1, 2); Position foodpos(1, 2);

        pass = (Game::reachSurroundings(simpos, foodpos) == ActionType::E);

        g.addAdvantage(0, 1); Position adpos(0, 1);

        pass = pass && (Game::reachSurroundings(simpos, adpos) == ActionType::N);

        Position newpos(2, 0);

        pass = pass && (Game::reachSurroundings(simpos, newpos) == ActionType::SW);
    }
    ec.result(pass);
}
 //operator+=, operator-=, operator*=, operator/=
void test_point_CAO(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Compound arithmetic ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("plus equals (two points)");

        {
            Point p1(50), p2(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i;
                p2[i] = i + 1;
            }

            Point p3(50);
            p3 += p1 += p2;
            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p3[i] == 2 * i + 1);
            }
            ec.result(pass);
        }

        ec.DESC("minus equals (two points)");

        {
            Point p1(50), p2(50), p3(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i;
                p2[i] = i + 1;
                p3[i] = 3 * i + 1;
            }

            p3 -= p2 -= p1;
            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p3[i] == 3 * i);
            }
            ec.result(pass);
        }

        ec.DESC("times equals (point and double)");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i;
            }

            p1 *= 3.14;

            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p1[i] == 3.14 * i);
            }
            ec.result(pass);
        }

        ec.DESC("divide equals (point and double)");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = 100.0 * i;
            }

            p1 /= 3.14;

            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p1[i] == 100.0 * i / 3.14);
            }
            ec.result(pass);
        }
    }
}
 //operator+, operator-, operator*, operator/
void test_point_SAO(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Simple arithmetic ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("plus (two points)");

        {
            Point p1(50), p2(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i;
                p2[i] = i + 1;
            }

            Point p3 = p1 + p2;
            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p3[i] == 2 * i + 1);
            }
            ec.result(pass);
        }

        ec.DESC("minus (two points)");

        {
            Point p1(50), p2(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i + 1;
                p2[i] = 2 * i - 1;
            }

            Point p3 = p2 - p1;
            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p3[i] == i - 2);
            }
            ec.result(pass);
        }

        ec.DESC("times (point and double)");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = i;
            }

            Point p2 = p1 * 3.14;

            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p2[i] == 3.14 * i);
            }
            ec.result(pass);
        }

        ec.DESC("divide (point and double)");

        {
            Point p1(50);

            for (int i = 0; i < 50; i++) {
                p1[i] = 100.0 * i;
            }

            Point p2 = p1 / 3.14;

            pass = true;
            for (int i = 0; i < 50; i++) {
                pass = pass && (p2[i] == 100.0 * i / 3.14);
            }
            ec.result(pass);
        }
    }
}
// setValue, getValue, operator[]
void test_point_getsetelem(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Point - Get/set element ---");

    for (int run = 0; run < numRuns; run ++)
    {
        ec.DESC("values default to zero");

        {
            Point p(50);

            // Check zeros
            pass = true;
            for (int i = 0; i < 50; i ++) {
                pass = pass &&
                        (p.getValue(i) == 0.0) &&
                        (p[i] == 0.0);
            }
            ec.result(pass);
        }

        ec.DESC("setValue, getValue (0-indexed)");

        {
            Point p(20);

            // Set values
            for (int i = 0; i < 20; i ++)
                p.setValue(i, 13.43 * i * i + 4.567 * i + 1.234567);

            // Check values
            pass = true;
            for (int i = 0; i < 20; i ++)
                pass = pass &&
                        (p.getValue(i) == (13.43 * i * i + 4.567 * i + 1.234567)) &&
                        (p[i] == (13.43 * i * i + 4.567 * i + 1.234567));
            ec.result(pass);
        }

        ec.DESC("operator[] (0-indexed)");

        {
            Point p(5);

            // Set values
            for (int i = 0; i < 5; i ++)
                p[i] = 1000000.43 * i * i + 400000.567 * i + 10000.234567;

            // Check values
            pass = true;
            for (int i = 0; i < 5; i ++)
                pass = pass &&
                       (p.getValue(i) == (1000000.43 * i * i + 400000.567 * i + 10000.234567)) &&
                       (p[i] == (1000000.43 * i * i + 400000.567 * i + 10000.234567));
            ec.result(pass);
        }
    }
}
// add, remove
void test_cluster_addremove(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Add/remove points ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("add and check with size getter");

        {
            Cluster c1;
            c1.add(Point(50));
            c1.add(Point(50));
            c1.add(Point(50));

            pass = (c1.getSize() == 3);

            ec.result(pass);
        } // by default, points will get released here


        ec.DESC("add, remove, and check with size getter");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);
            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            c1.remove(p1); c1.remove(p2); c1.remove(p3);

            pass = (c1.getSize() == 0);
            if (!pass) {
                std::cout << std::endl;
                std::cout << c1 << std::endl;
                std::cout << std::endl;
            }

            ec.result(pass);
        } // by default, points will get released here

        ec.DESC("add, check with cluster equality, remove");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);

            Cluster c1, c2;
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p1); c2.add(p2); c2.add(p3);

            pass = (c1 == c2);
            // don't forget to remove the points from one
            // of the clusters to avoid the "double delete"
            c2.remove(p1); c2.remove(p2); c2.remove(p3);

            ec.result(pass);
        }

        ec.DESC("check point after add and remove");

        {
            Point   p1(10);

            for (int i = 0; i < 10; i++)
                p1[i] = 5.4 * i * i + 3.4 * i + 1.6;

            Cluster c1;
            c1.add(p1);
            Point p2 = c1.remove(p1);

            pass = (p1 == p2);

            ec.result(pass);
        }
    }
}
// operator+, operator-, different rhs
void test_cluster_SAO(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Simple arithmetic ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("plus (Cluster and Point)");

        {
            Cluster c1, c2;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50);
            c1.add(p1); c1.add(p2); c1.add(p3);

            c2 = c1; c2.add(p4);
            Cluster c3 = c1 + p4;

            pass = (c3 == c2);

            ec.result(pass);

        }

        ec.DESC("minus (Cluster and Point)");

        {
            Cluster c1, c2;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50);
            c1.add(p1); c1.add(p2); c1.add(p3); c1.add(p4);

            c2 = c1; c2.remove(p4);
            Cluster c3 = c1 - p4;

            pass = (c3 == c2);

            ec.result(pass);
        }

        ec.DESC("plus (Cluster union)");

        {
            Cluster c1, c2;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p4); c2.add(p5);

            Cluster c3;
            c3.add(p1); c3.add(p2); c3.add(p3); c3.add(p4); c3.add(p5);

            Cluster c4 = c1 + c2;

            pass = (c4 == c3);

            ec.result(pass);
        }

        ec.DESC("minus (Cluster difference)");

        {
            Cluster c1, c2;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p3); c2.add(p4); c2.add(p5);

            Cluster c3;
            c3.add(p1); c3.add(p2);

            Cluster c4 = c1 - c2;

            pass = (c4 == c3);

            ec.result(pass);
        }
    }
}
// ascending pseudo-lexicographic order
void test_cluster_order(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Order ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("points in a cluster are sorted");

        {
            Point p1(5), p2(5), p3(5), p4(5), p5(5);
            p1[0] = 1;
            p2[1] = 1;
            p3[2] = 1;
            p4[3] = 1;
            p5[4] = 1;

            Cluster c;
            c.add(p1);
            c.add(p2);
            c.add(p4);
            c.add(p3);
            c.add(p5);

            pass = (c[0] == p5)
                   && (c[1] == p4)
                   && (c[2] == p3)
                   && (c[3] == p2)
                   && (c[4] == p1);

            if (!pass) {
                std::cout << std::endl;
                std::cout << c << std::endl;
                std::cout << std::endl;
            }

            ec.result(pass);
        }

        ec.DESC("ascending pseudo-lexicographic order");

        {
            Point p1(5), p2(5), p3(5), p4(5), p5(5);
            p1[0] = 1;
            p2[1] = 1;
            p3[2] = -1;
            p4[3] = 1;
            p5[4] = -1;

            Cluster c;
            c.add(p1);
            c.add(p2);
            c.add(p4);
            c.add(p3);
            c.add(p5);

            pass = (c[0] == p3)
                   && (c[1] == p5)
                   && (c[2] == p4)
                   && (c[3] == p2)
                   && (c[4] == p1);

            if (!pass) {
                std::cout << std::endl;
                std::cout << c << std::endl;
                std::cout << std::endl;
            }

            ec.result(pass);
        }
    }
}
// Piece interaction operator*
void test_piece_interaction(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Piece - Interaction ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("3x3, manual, two agents with equal energy");

        {
            Game g;

            Simple s0(g, Position(2, 0), Game::STARTING_AGENT_ENERGY);
            Strategic s1(g, Position(1, 1), Game::STARTING_AGENT_ENERGY);

            Piece *pieces[2] = { &s0, &s1 };
            Piece &p0 = *pieces[0], &p1 = *pieces[1];
            p1 * p0;
            pass = (! p0.isViable()) && (! p1.isViable());

            ec.result(pass);
        }


        ec.DESC("3x3, manual, two agents with inequal energy");

        {
            Game g;

            Simple s0(g, Position(2, 0), Game::STARTING_AGENT_ENERGY);
            Strategic s1(g, Position(1, 1), Game::STARTING_AGENT_ENERGY * 1.1);

            Piece *pieces[2] = { &s0, &s1 };
            Piece &p0 = *pieces[0], &p1 = *pieces[1];
            p1 * p0;
            pass = (! p0.isViable()) && p1.isViable();

            ec.result(pass);
        }

        ec.DESC("3x3, manual, a simple agent and a resource");

        {
            Game g;

            Food s0(g, Position(2, 0), Game::STARTING_RESOURCE_CAPACITY);
            Strategic s1(g, Position(1, 1), Game::STARTING_AGENT_ENERGY * 1.1);

            Piece *pieces[2] = { &s0, &s1 };
            Piece &p0 = *pieces[0], &p1 = *pieces[1];
            p1 * p0;
            pass = (! p0.isViable()) && p1.isViable();

            ec.result(pass);
        }

        ec.DESC("3x3, manual, no interaction between resources");

        {
            Game g;

            Food s0(g, Position(2, 0), Game::STARTING_RESOURCE_CAPACITY);
            Food s1(g, Position(1, 1), Game::STARTING_RESOURCE_CAPACITY);

            Piece *pieces[2] = { &s0, &s1 };
            Piece &p0 = *pieces[0], &p1 = *pieces[1];
            p1 * p0;
            pass = p0.isViable() && p1.isViable();

            ec.result(pass);
        }
    }
}
//// operator=
void test_cluster_assignment(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Assign ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("simple assignment");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);

            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            Cluster c2 = c1;

            pass = (c1 == c2);

            ec.result(pass);
        }


        ec.DESC("assignment causing deletion");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);

            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            Cluster c2;
            // add some other points
            c2.add(Point(10));
            c2.add(Point(10));
            c2.add(Point(10));

            c2 = c1;

            pass = (c1 == c2);

            ec.result(pass);
        }

        ec.DESC("chained assignment");

        {
            Point   p1(10),
                    p2(10),
                    p3(10);

            Cluster c1;
            c1.add(p1); c1.add(p2); c1.add(p3);
            Cluster c2 = c1;
            Cluster c3 = c2;
            Cluster c4 = c3;

            pass = (c1 == c4);

            ec.result(pass);
        }
    }
}
// operator+=, operator-=, different rhs
void test_cluster_CAO(ErrorContext &ec, unsigned int numRuns) {
    bool pass;

    // Run at least once!!
    assert(numRuns > 0);

    ec.DESC("--- Test - Cluster - Compound arithmetic ---");

    for (int run = 0; run < numRuns; run++) {

        ec.DESC("plus equals (Cluster and Point) check with non-equality");

        {
            Cluster c1, c2;
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 6.75 * i * i + 5.45 * i + 1.15;

            c1 += p1;

            pass = !(c1 == c2);

            ec.result(pass);
        }

        ec.DESC("plus equals (Cluster and Point) check with size getter");

        {
            Cluster c1;
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 6.75 * i * i + 5.45 * i + 1.15;

            c1 += p1;

            pass = (c1.getSize() == 1);

            ec.result(pass);
        }

        ec.DESC("minus equals (Cluster and Point) check with non-equality");

        {
            Cluster c1, c2;
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 6.75 * i * i + 5.45 * i + 1.15;

            c1 += p1;
            pass = !(c1 == c2);

            c1 -= p1;
            pass = (c1 == c2);

            ec.result(pass);
        }

        ec.DESC("minus equals (Cluster and Point) check with size getter");

        {
            Cluster c1;
            Point p1(50);

            for (int i = 0; i < 50; i++)
                p1[i] = 6.75 * i * i + 5.45 * i + 1.15;

            c1 += p1;
            pass = (c1.getSize() == 1);

            c1 -= p1;
            pass = (c1.getSize() == 0);

            ec.result(pass);
        }

        ec.DESC("plus equals (Cluster union) no common points");

        {
            Cluster c1, c2, c3;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p4); c2.add(p5);

            // create a union to compare to
            c3.add(p1); c3.add(p2); c3.add(p3);
            c3.add(p4); c3.add(p5);

            c1 += c2;

            pass = (c1 == c3);

            ec.result(pass);
        }

        ec.DESC("plus equals (Cluster union) one common point");

        {
            Cluster c1, c2, c3;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p3); c2.add(p4); c2.add(p5);

            // create a union to compare to
            c3.add(p1); c3.add(p2); c3.add(p3);
            c3.add(p4); c3.add(p5);

            c1 += c2;

            pass = (c1 == c3);

            ec.result(pass);
        }

        ec.DESC("plus equals (Cluster union) two equal clusters");

        {
            Cluster c1, c2;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3); c1.add(p4); c1.add(p5);
            c2.add(p1); c2.add(p2); c2.add(p3); c2.add(p4); c2.add(p5);

            c1 += c2;

            pass = (c1 == c2);

            ec.result(pass);
        }

        ec.DESC("minus equals (asymmetric Cluster difference) no common points");

        {
            Cluster c1, c2, c3;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p4); c2.add(p5);

            c3 = c1;
            c1 -= c2;

            pass = (c1 == c3);

            ec.result(pass);
        }

        ec.DESC("minus equals (asymmetric Cluster difference) one common point");

        {
            Cluster c1, c2, c3;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3);
            c2.add(p3); c2.add(p4); c2.add(p5);

            // Prepare a difference to compare to
            c3.add(p1); c3.add(p2);
            c1 -= c2;

            pass = (c1 == c3);

            ec.result(pass);
        }

        ec.DESC("minus equals (asymmetric Cluster difference) two equal clusters");

        {
            Cluster c1, c2, c3;
            Point   p1(50),
                    p2(50),
                    p3(50),
                    p4(50),
                    p5(50);
            c1.add(p1); c1.add(p2); c1.add(p3); c1.add(p4); c1.add(p5);
            c2.add(p1); c2.add(p2); c2.add(p3); c2.add(p4); c2.add(p5);

            c1 -= c2;

            pass = (c1 == c3); // c1 should be empty

            ec.result(pass);
        }
    }
}