Minimax algorithm for Specker Game - c++

I am creating an minimax player for a game called Specker in c++.
The rules are simple:
There are p players (0 to p - 1) and n heaps (0 to n - 1)
Starting with player 0 each player takes k > 0 coins from a heap x and places m coins (0 <= m < k) on heap y
The winning player is the one which plays last when all coins from all heaps are removed
So I have created the game and some player classes (GreedyPlayer, SpartanPlayer etc.) but they all are a little bit predictable on what they will do. They aren't clever.
so i am creating a player who plays according to minimax (pt18a038)code compiles fine but the program stops responding on execution.
the clever player class:
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;
}
};
Here is my code for the rest of the game(it's tested and works fine)
#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);
}
Updated

I see a couple of issues in your minimax program, some are serious in nature :
1) Use Alpha Beta pruning to reduce the size of the search tree.
2) There is no proper boundary condition (if depth >5 return score or something) in the minimax recursive call ( see my code snippet for details), the CPU may hang while calling.
3)Your leaf node evaluation is weak, so the evaluated moves , in spite of using minimax algorithm, is not likely to be intelligent ones.
4)To increase the search speed, you may use multi-threading only at the top level branch.
5) If a leaf node gives a winning move while maximizing, you may skip further evaluation by returning a high score.
6) Once your code is functional, refer it at code-review with 'ai' tag , rather than at SO, for more detailed analysis.
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
}

Related

Modifing an object by another class

My purpose is to change the tank (an object of first class) by another class (the odometer). So I try to passing by reference, its working when I pass directly object to constructor but its doesn't working when I make an object first then passing object by a method(setOdoIndex). Can someone have a way to do make a method to pass these parameters
#include <iostream>
using namespace std;
class FuelGauge {
protected:
double galls;
double check(double) const;
double checkFuel(double) const;
public:
FuelGauge(double galls){
check(galls);
this->galls = galls;
}
FuelGauge(){
*this = FuelGauge(0);
}
double getFuelLeft() const{
return galls;
}
FuelGauge operator++(){
if (galls > 15) throw "Tank max capacity is 15 gallon";
++galls;
return *this;
}
FuelGauge operator--(){
if (galls == 0) throw "Tank is empty";
--galls;
return *this;
}
void refuel(){
galls = 15;
}
};
double FuelGauge::check(double n) const {
if (n < 0) throw "Dont accepted negative value!";
if (n > 15) throw "Tank max capacity is 15 gallon";
return n;
}
class Odometer{
private:
int odo;
FuelGauge &tank;
void calOdo() {
if (odo > 999999) {odo = 0;};
}
public:
Odometer(int odo, FuelGauge &tank):tank(tank) {
this->odo = odo;
this->tank = tank;
}
Odometer():tank(tank) {
odo = 0;
}
int getOdoIndex() const{
return odo;
}
void setOdoIndex(int odo, FuelGauge &tank) {
this->odo = odo;
this->tank = tank;
}
void carDrive() {
--tank;
calOdo();
++odo;
}
};
int main() {
FuelGauge tank;
cout << "--Fill the tank--" << endl;
try {
for (int i = 0; i < 15; i++) {
++tank;
}
}
catch(const char* e) {
cerr << e << '\n';
}
cout << "\n--Car run--" << endl;
Odometer odo1(0, tank);
try {
for(int i = 0; i < 16; i++) {
cout << "Index of odometer: " << odo1.getOdoIndex() << endl;
cout << "Fuel left: " << tank.getFuelLeft() << endl;
odo1.carDrive();
}
}
catch(const char* e) {
cerr << e << '\n';
}
return 0;
}

can't acces member functions of a global object

So i'm trying to make a game inspired by a youtube tutorial but a bit more complex , i had the snake created in the setup function but i moved it since i couldn't acces it's members from other functions and with it being global i can't acces it at all , how can i fix it ?
in the stuff.h header i have colio.h and iostream
bool gameOver;
std::array<std::array<int, 20>, 20> grid;
snake jerry(15, 9);
class object
{
protected:
int m_x, m_y;
object(int y, int x) :m_x(x), m_y(y) {};
public:
const int getX() { return m_x; };
const int getY() { return m_y; };
};
class snake:public object
{
private:
enum direction
{
STOP,
LEFT,
RIGHT,
UP,
DOWN,
};
direction dir;
public:
snake(int y, int x) :object(y, x) {};
const void moveSnake(char c)
{
if (c == 'a') { dir = LEFT; };
if (c == 'd') { dir = RIGHT; };
if (c == 's') { dir = DOWN; };
if (c == 'w') { dir = UP; }
};
const void changeCoords(int y,int x) { m_x = x, m_y = y; };
};
class fruit :public object
{
private:
public:
fruit(int y, int x) :object(y,x) {};
};
void setup()
{
for (int j = 0; j < 20; ++j)
{
for (int i = 0; i < 20; ++i)
{
if (j == 0 || j == 19 || i == 0 || i == 19) // 0=space , 1=# , 2=snake, 3= fruit , 4= snake tail
{
grid[j][i] = 1;
}
else
{
grid[j][i] = 0;
}
}
}
grid[jerry.getY()][jerry.getX]=2;
fruit fr(rand() % 20, rand() % 20);
grid[fr.getY()][fr.getX()] = 3;
}
void draw()
{
for (int j = 0; j < 20; ++j)
{
for (int i = 0; i < 20; ++i)
{
if (grid[j][i]==1)
{
std::cout << "#";
}
else if (grid[j][i]==0)
{
std::cout << " ";
}
else if (grid[j][i] == 2)
{
std::cout << "O";
}
else if (grid[j][i] == 3)
{
std::cout << "F";
}
}
std::cout << std::endl;
}
system("cls");
}
void input()
{
if (_kbhit())
{
jerry.moveSnake(_getch);
}
}
void logic()
{
}
int main()
{
setup();
while (gameOver==false)
{
draw();
input();
logic();
}
return 0;
}

My program prints random cards, but it will not recognize when there is a repeat card

I am a beginner with C++ and have tried a few different things, but no matter what I try it doesn’t seem to recognize when a card has already been drawn...
I have tried to utilize to bool isDrawn, but after a few attempts of that with no success I am not 100% sure where to go from here
class Card {
public:
string suitName;
int cardNumber;
void printCard() {
if (cardNumber == 1) {
cout << "Ace";
} else if (cardNumber == 11) {
cout << "Jack";
} else if (cardNumber == 12) {
cout << "Queen";
} else if (cardNumber == 13) {
cout << "King";
} else
cout << cardNumber;
cout << " of " << suitName << endl;
}
bool isDrawn = false;
};
class Deck {
public:
Card deck[52];
void makeDeck(){
int counter = 0;
string suits[] = { "Spades", "Hearts", "Clubs", "Diamonds" };
string face[] = { "Ace", "Jack", "Queen", "King" };
for (int i = 0; i <= 3; i++) {
for (int j = 0; j < 13; j++) {
deck[counter].isDrawn = false;
deck[counter].cardNumber = (j + 1);
deck[counter].suitName = suits[i];
counter++;
}
}
}
Card drawCard() {
int randcard;
do {
randcard = rand() % 52;
} while (deck[randcard].isDrawn == true);
return deck[randcard];
}
};
class Player {
public:
vector<Card> hand;
void setName(string s){
name = s;
}
string printName(){
return name;
}
void printHand() {
for (int i = 0; i < hand.size(); i++){
hand.at(i).printCard();}
}
private:
string name;
};
int main() {
Deck my_deck;
Player p1;
p1.setName("HAL 9000");
cout << p1.printName() << endl;
my_deck.makeDeck();
p1.hand.push_back(my_deck.drawCard());
p1.hand.push_back(my_deck.drawCard());
p1.hand.push_back(my_deck.drawCard());
p1.printHand();
cout << endl;
}

Copy Constructor for dynamically allocated array

Trying to familiarize myself with the "Rule of 3" and Im having trouble getting a Copy Constructor to work. One of the class private members is returning 0 when it should have a value of 3.
Im not sure as to why when the Copy Constructor function is performed, a value of 0 is supplied to that classes private member. The member in question is theSize which is returned via the size() function in class.cpp.
class.h
class Catalog {
public:
Catalog (int maxCapacity = 100)
int size() const;
int capacity() const;
void add (Book b);
Catalog(const Catalog& c);
~Catalog();
Catalog& operator= (constCatalog& c) {
if (this != &c) {
delete[] books;
books = new Book[theCapacity];
*books = *(c.books);
}
return *this;
}
private:
Book* books;
int theCapacity;
int theSize;
};
class.cpp
Catalog::Catalog(int maxCapacity) {
theCapacity = maxCapacity;
theSize = 0;
books = new Book[theCapacity];
}
int Catalog::size() const {
return theSize();
}
int Catalog::capacity() const {
return theCapacity;
}
void Catalog::add (Book b)
{
if (theSize < theCapacity || contains(b.getID())) {
if (theSize == 0) {
books[0] = b;
theSize++;
}
else {
if (!contains(b.getID())) {
int i = theSize;
for (; i && b < books[i-1]; --i) {
books[i] = books[i - 1];
}
books[i] = b;
for (; i; --i) {
books[i - 1] = books[i - 1];
}
theSize++;
}
else {
for (int i = 0; i < theSize; ++i) {
if (b == books[i]) {
books[i] = b;
}
}
}
}
// Debugging only
/*for (int i = 0; i < theSize; i++) {
//cout << books[i] << endl;
}*/
}
}
bool Catalog::contains(std::string bookID) const
{
for (int i = 0; i < theSize; ++i)
{
if (books[i].getID() == bookID)
return true;
}
return false;
}
Catalog::Catalog(const Catalog& c) {
books = new Book[c.theSize];
for (int i = 0; i < c.theSize; i++) {
books[i] = c.books[i];
}
Catalog::~Catalog() {
delete[] books;
}
Later in main.cpp when I call c1.size() where c1 is the result of return c in another function that through use of the debugger comes from the Copy Constructor and then goes to the Destructor. However, c1.size() is returning as 0 though the Copy Constructor theSize = c.size() has a value of 3 when stepped through.
book.cpp
using namespace std;
/**
* Create a book.
*
* #param id the Gutenberg ID for this book
* #param authorInfo the author of the book
* #param title the title of the book
*/
Book::Book (std::string theId, std::string authorInfo, std::string theTitle)
: id(theId), authorName(authorInfo), title(theTitle)
{
}
bool Book::operator< (const Book& b) const
{
return id < b.id;
}
bool Book::operator== (const Book& b) const
{
return (id == b.id);
}
std::ostream& operator<< (std::ostream& out, const Book& b)
{
cout << b.getID() << "\t"
<< b.getAuthor() << "\t"
<< b.getTitle();
return out;
}
std::istream& operator>> (std::istream& in, Book& b)
{
string line;
getline (in, line);
if (!in.good())
return in;
int tab1 = line.find ("\t");
int tab2 = line.find ("\t", tab1+1);
string id = line.substr(0, tab1);
string author = line.substr (tab1+1, tab2-tab1-1);
string title = line.substr(tab2+1);
b.setID (id);
b.setAuthor (author);
b.setTitle (title);
return in;
}
main.cpp
using namespace std;
Catalog readCatalog(const string& fileName)
{
Catalog c;
ifstream in (fileName);
in >> c;
in.close();
return c;
}
Catalog mergeCatalogs (const Catalog& cat1, const Catalog& cat2)
{
Catalog result (cat1.size() + cat2.size());
int i = 0;
int j = 0;
while (i < cat1.size() && j < cat2.size())
{
Book b1 = cat1.get(i);
Book b2 = cat2.get(j);
if (b1.getID() < b2.getID())
{
result.add(b1);
++i;
}
else
{
result.add(b2);
++j;
}
}
while (i < cat1.size())
{
result.add(cat1.get(i));
++i;
}
while (j < cat2.size())
{
result.add(cat2.get(j));
++j;
}
return result;
}
void mergeCatalogFiles (const string& catalogFile1, const string& catalogFile2)
{
Catalog c1, c2;
c1 = readCatalog(catalogFile1);
cout << catalogFile1 << " contained " << c1.size() << " books." << endl;
c2 = readCatalog(catalogFile2);
cout << catalogFile2 << " contained " << c2.size() << " books." << endl;
Catalog c3 = mergeCatalogs (c1, c2);
cout << "Their merge contains " << c3.size() << " books." << endl;
cout << c3 << flush;
}
int main (int argc, char** argv)
{
if (argc != 3)
{
cerr << "Usage: " << argv[0] <<
"catalogFile1 catalogFile2" << endl;
return -1;
}
string file1 = argv[1];
string file2 = argv[2];
mergeCatalogFiles (file1, file2);
if (Counted::getCurrentCount() == 0)
{
cout << "No memory leak detected." << endl;
return 0;
}
else
{
cout << "Memory leak detected: " << Counted::getCurrentCount() << endl;
return -2;
}
}
Follow rule of zero: use std::vector<Book> to replace the array pointer and the size.
Your capacity is a limit on the size.
When at capacity. use equal range to find where to insert, replace last element then std rotate.
Managing both resources and business logic in the same class is bug prone. Do one thing at a time.
Try something more like this instead:
class Catalog
{
public:
Catalog (int maxCapacity = 100);
Catalog(const Catalog& c);
~Catalog();
int size() const;
int capacity() const;
void add (const Book &b);
Book* find(const std::string &bookID) const;
Catalog& operator= (Catalog c);
private:
Book* books;
int theCapacity;
int theSize;
void swap(Catalog &c);
};
#include "class.h"
#include <algorithm>
Catalog::Catalog(int maxCapacity)
{
theCapacity = maxCapacity;
theSize = 0;
books = new Book[theCapacity];
}
Catalog::Catalog(const Catalog& c)
{
theCapacity = c.theCapacity;
books = new Book[theCapacity];
for(int i = 0; i < c.theSize;; ++i)
books[i] = c.books[i];
theSize = c.theSize;
}
Catalog::~Catalog()
{
delete[] books;
}
Catalog& Catalog::operator= (const Catalog &c)
{
if (this != &c)
Catalog(c).swap(*this);
return *this;
}
void Catalog::swap(Catalog &c)
{
std::swap(books, c.books);
std::swap(theSize, c.theSize);
std::swap(theCapacity, c.theCapacity);
}
int Catalog::size() const
{
return theSize;
}
int Catalog::capacity() const
{
return theCapacity;
}
void Catalog::add (const Book &b)
{
Book *book = find(b.getID());
if (book) {
*book = b;
}
else if (theSize < theCapacity)
{
int i;
for (i = theSize; i && b < books[i-1]; --i) {
books[i] = books[i - 1];
}
books[i] = b;
++theSize;
}
// Debugging only
/*
for (int i = 0; i < theSize; ++i) {
cout << books[i] << endl;
}
*/
}
Book* Catalog::find(const std::string &bookID) const
{
for (int i = 0; i < theSize; ++i)
{
if (books[i].getID() == bookID)
return &books[i];
}
return 0;
}
That being said, this would be much simpler and easier to manage if you use std::vector and STL algorithms. Let the STL do the hard work for you:
#include <vector>
class Catalog
{
public:
Catalog (int initialCapacity = 100);
int size() const;
int capacity() const;
void add (const Book &b);
Book* find(const std::string &bookID) const;
private:
std::vector<Book> books;
};
#include "class.h"
#include <algorithm>
Catalog::Catalog(int initialCapacity)
{
books.reserve(initialCapacity);
}
int Catalog::size() const
{
return books.size();
}
int Catalog::capacity() const
{
return books.capacity();
}
void Catalog::add (const Book &b)
{
Book *book = find(b.getID());
if (book) {
*book = b;
}
else {
books.insert(std::upper_bound(books.begin(), books.end(), b), b);
}
// Debugging only
/*
for (Book &book: books) {
cout << book << endl;
}
*/
}
Book* Catalog::find(const std::string &bookID) const
{
auto iter = std::find_if(books.begin(), books.end(), [&bookID](const Book &b){ return (b.getID() == bookID); });
if (iter != books.end())
return &*iter;
return 0;
}

Huffman Encoding priority queue

I'm working on an assignment where I should write an encoding and decoding application for the Huffman algorithm, based on a priority queue. We have to read a file, count the frequencies of the letters and then start the algorithm. I have the following problem:
My counting function works fine but it stores the frequency of every letter in an array - even if it's zero. But if I want to use that array to build my min heap I get major problems because of the zeros. Therefore I need to find a way to 'eliminate' them. I can't just skip them because then the min heap algorithm doesn't work anymore (wrong neighbours). So I wanted to transfer all non-zero entries in a vector and use the vector instead of the array. But there I always get an error that tells me that there's a problem with the vector size. I don't really know how to deal with that problem. (My min heap still uses the array because I can't even transfer the entries in a vector).
(Please ignore the main I was just trying stuff there!)
using namespace std;
struct huffman_node
{ char data;
int frequency;
bool vector;
huffman_node *left;
huffman_node *right;
};
void swap_huffman_nodes(huffman_node &a, huffman_node &b)
{ char store_data = a.data;
int store_frequency = a.frequency;
a.data = b.data;
a.frequency=b.frequency;
b.data = store_data;
b.frequency = store_frequency;
huffman_node *store_left = a.left;
huffman_node *store_right= a.right;
a.left = b.left;
a.right = b.right;
b.left = store_left;
b.right = store_right;
}
void print_node (huffman_node a)
{ cout << a.data << a.frequency << endl;
}
string line;
huffman_node Table[52];
vector <huffman_node> non_zero;
void build_table()
{ for (int i=1; i<27; i++)
{ Table[i].data = (char) (i+64);
Table[i].left = NULL;
Table[i].right = NULL;
}
for (int i=27; i<53; i++)
{ Table[i].data = (char) (i+70);
Table[i].left = NULL;
Table[i].right = NULL;
}
}
int counter =0;
void count(){
ifstream yourfile ("example.txt");
if (yourfile.is_open())
{
while ( getline (yourfile,line) )
{
/*cout << line << '\n'; */
unsigned long z=line.length();
int i=0;
while ( i < z)
{ /* cout << line[i] << endl; */
for (int j=65; j<91; j++)
{ if ((int) line[i] == j)
{ int k=-64+j;
Table[k].frequency++;
}
}
for (int j=97; j<123; j++)
{ if ((int) line[i] == j)
{ int k=-70+j;
Table[k].frequency++;
}
}
i++;
}
}
for (int i=1; i<53; i++)
{ if (Table[i].frequency!=0)
{ non_zero.push_back(Table[i]);
counter ++;
}
}
yourfile.close();
}
else cout << "Unable to open file";
}
class heap{
public:
void buildheap()
{
for (int i=1; i<53; i++)
{reheap(i);
};
}
void reheap(int new_index)
{ int parent_index = new_index/2;
while (parent_index > 0 && Table[parent_index].frequency > Table[new_index].frequency)
{ swap_huffman_nodes(Table[parent_index], Table[new_index]);
parent_index=parent_index/2;
new_index=new_index/2;
}
};
void delete_root()
{ int non_null_entries=0;
for (int i=1; i<53; i++)
{ if (Table[i].frequency!=-1) {non_null_entries++;};
}
swap_huffman_nodes(Table[1],Table[non_null_entries]);
Table[non_null_entries].frequency=-1;
non_null_entries--;
rebuild_heap_root_deletion(1, non_null_entries);
}
void rebuild_heap_root_deletion(int new_root,int non_null_entries){
int n;
if (2 * new_root > non_null_entries){
return;
}
if (2 * new_root + 1 <= non_null_entries
&& Table[2*new_root+1].frequency < Table[2*new_root].frequency){
n = 2 * new_root + 1;
} else {
n = 2 * new_root;
}
if (Table[new_root].frequency > Table[n].frequency){
swap_huffman_nodes(Table[new_root], Table[n]);
rebuild_heap_root_deletion(n, non_null_entries);
}
}
void add_element(huffman_node new_heap_element)
{ for (int i=52; i>0;i-- )
{ if (Table[i].frequency==-1 && Table[i-1].frequency!=-1)
{ Table[i]=new_heap_element;
reheap(i);
break;
}
}
}
void print_Table()
{
for (int i=1; i<53; i++)
{ /*if (Table[i].frequency != -1) */
cout << Table[i].frequency << " , " << Table[i].data << endl;
}
}
bool empty_heap() // a heap is empty here if there are only "invalid huffman nodes" in it except the first one that contains all information.
{ for (int i=2; i < 53; i++)
{ if (Table[i].frequency!=-1)
{ return false;}
}
return true;
}
};
int main(){
ofstream myfile ("example.txt");
if (myfile.is_open())
{
myfile << "Flori ist ein Koala.";
myfile << "";
myfile.close();
}
else cout << "Unable to open file";
build_table();
count();
heap allan;
cout << "\n";
allan.buildheap();
allan.print_Table();
int i=0;
/*while(i<500)
{
huffman_node base_1 = Table[1];
allan.delete_root();
huffman_node base_2 = Table[1];
allan.delete_root();
huffman_node parent;
parent.data = '/';
parent.frequency = base_1.frequency + base_2.frequency;
parent.left = &base_1;
parent.right = &base_2;
allan.add_element(parent);
i++;
}
return 0;
}