Ir ao conteúdo
  • Cadastre-se
Edmorte

Exemplo C++ - Hanoi não recursivo

Recommended Posts

Essa é uma nova versão que eu refinei da antiga, discutindo com os colegas do fórum me levaram a ter ideias.

Melhorias:

- Uso do map agora não faz mais cast (nem sei porque fazia antes!);

- Não existem mais HARDCODES das torres no código, ou seja, se você alterar no ENUM nada quebra;

- Melhorias no design, agora sim a engine não conhece como funcionam as torres e apenas consome. (melhoria no encapsulamento);

- Coloquei mais recursos do C++ para as pessoas verem coisas novas, algumas situações são forçadas por causa disso!!!

Espero que gostem, esse está bem melhor que antes.

28/12/13 Edit: Correção do post increment, estava fazendo como o pré.


#include <iostream>
#include <vector>
#include <string>
#include <map>

namespace HanoiGame
{
using namespace std;

enum class Tower : char
{
TowerA = 'A',
TowerB = 'B',
TowerC = 'C'
};

class GameEngine
{
class Hanoi
{
vector<int> tower;

public:
// Construtores para demonstrar, não há necessidade de defini-los nesse caso.
Hanoi() = default;
Hanoi(const Hanoi& t) : tower{ t.tower } {}
// Fim construtores

bool isValidMove(int disc) const;
int size() const;
int peek() const;
int pop();
void push(int disc);
};

int discs;

public:
map<Tower, Hanoi> tower;

explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD) const;
bool isGameFinished() const;
string HanoiGame::GameEngine::print() const;
};

ostream& operator<<(ostream& os, const HanoiGame::GameEngine& obj);
Tower& operator++(Tower&);
Tower operator++(Tower& t, int);
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(char tower)
{
return ((Tower)tower >= Tower::TowerA && (Tower)tower <= Tower::TowerC);
}

void playGame()
{
HanoiGame::GameEngine game{};
char origem, destino;

do
{
cout << game;

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) origem, (Tower) destino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

cout << game;
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;

for (size_t i = discs; i > 0; --i)
{
tower[Tower::TowerA].push(i);
}

tower[Tower::TowerB] = Hanoi{};
tower[Tower::TowerC] = Hanoi{};
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD) const
{
return tower.find(towerO)->second.size() > 0 &&
tower.find(towerD)->second.isValidMove(tower.find(towerO)->second.peek());
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
tower[towerD].push(tower[towerO].pop());

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished() const
{
return tower.find(Tower::TowerA)->second.size() == 0 &&
(
tower.find(Tower::TowerB)->second.size() == discs ||
tower.find(Tower::TowerC)->second.size() == discs
);
}

string HanoiGame::GameEngine::print() const
{
string str;
std::map<Tower, Hanoi> m{ tower };

for (char i = discs; i > 0; --i)
{
Tower t = Tower::TowerA;

do
{
str += (m.find(t)->second.size() >= i ?
std::to_string(m.find(t)->second.pop()) :
"|");

str += '\t';
} while (t++ < Tower::TowerC);

str += '\n';
}

return str;
}

std::ostream& HanoiGame::operator << (std::ostream& os, const HanoiGame::GameEngine& obj)
{
os << obj.print();

return os;
}

Tower& HanoiGame::operator++(Tower& t)
{
using namespace HanoiGame;

return t = (t == Tower::TowerC ? Tower::TowerA : static_cast<Tower>((char) t + 1));
}

Tower HanoiGame::operator++(Tower& t, int)
{
using namespace HanoiGame;

Tower old{ t };

t = (t == Tower::TowerC ? Tower::TowerA : static_cast<Tower>((char) t + 1));

return old;
}

int HanoiGame::GameEngine::Hanoi::size() const
{
return tower.size();
}

int HanoiGame::GameEngine::Hanoi::peek() const
{
return tower.back();
}

void HanoiGame::GameEngine::Hanoi::push(int disc)
{
tower.push_back(disc);
}

int HanoiGame::GameEngine::Hanoi::pop()
{
int disc = peek();
tower.pop_back();
return disc;
}

bool HanoiGame::GameEngine::Hanoi::isValidMove(int disc) const
{
return size() == 0 || disc < peek();
}

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Seu codigo ficou muito bom. Fiz algumas alterações nele. Veja como ficou

	enum class Tower : int
{
TowerA,
TowerB,
TowerC,
};

primeiro troquei para int.

class GameEngine
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;
vector<int> *tower[3];
int discs;

...

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;
this->tower[0] = &towerA;
this->tower[1] = &towerB;
this->tower[2] = &towerC;

for (size_t i = discs; i > 0; --i)
{
towerA.push_back(i);
}
}

nesta classe adicionei um vector<int> *tower[3]; para ter enumerado as torres.

vector<int>& HanoiGame::GameEngine::getTower(Tower tower)
{
return *this->tower[(int)tower];
}

neste momento posso tirar o switch.

E algumas alterações para aceitar A B C no codigo...

[hide]

#include <iostream>
#include <vector>
#include <string>

namespace HanoiGame
{
using namespace std;

enum class Tower : int
{
TowerA,
TowerB,
TowerC,
};

class GameEngine
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;
vector<int> *tower[3];
int discs;

vector<int>& getTower(Tower tower);
public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os) const;
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(int tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game(3);
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

int iorigem = origem - 'A';
int idestino = destino - 'A';

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) iorigem, (Tower) idestino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;
this->tower[0] = &towerA;
this->tower[1] = &towerB;
this->tower[2] = &towerC;

for (size_t i = discs; i > 0; --i)
{
towerA.push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (getTower(towerO).size() == 0)
{
return false;
}

if (getTower(towerD).size() == 0)
{
return true;
}

return getTower(towerO).back() < getTower(towerD).back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = getTower(towerO).back();
getTower(towerO).pop_back();
getTower(towerD).push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return towerA.size() == 0 && (towerB.size() == discs || towerC.size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os) const
{
for (size_t i = discs; i > 0; --i)
{
os << (towerA.size() >= i && towerA[i - 1] ? std::to_string(towerA[i - 1]) : "|");

os << '\t';

os << (towerB.size() >= i && towerB[i - 1] ? std::to_string(towerB[i - 1]) : "|");

os << '\t';

os << (towerC.size() >= i && towerC[i - 1] ? std::to_string(towerC[i - 1]) : "|");

os << endl;
}
}

vector<int>& HanoiGame::GameEngine::getTower(Tower tower)
{
return *this->tower[(int)tower];
}

[/hide]

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Seu codigo ficou muito bom. Fiz algumas alterações nele. Veja como ficou

	enum class Tower : int
{
TowerA,
TowerB,
TowerC,
};

primeiro troquei para int.

class GameEngine
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;
vector<int> *tower[3];
int discs;

...

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;
this->tower[0] = &towerA;
this->tower[1] = &towerB;
this->tower[2] = &towerC;

for (size_t i = discs; i > 0; --i)
{
towerA.push_back(i);
}
}

nesta classe adicionei um vector<int> *tower[3]; para ter enumerado as torres.

vector<int>& HanoiGame::GameEngine::getTower(Tower tower)
{
return *this->tower[(int)tower];
}

neste momento posso tirar o switch.

E algumas alterações para aceitar A B C no codigo...

[hide]

#include <iostream>
#include <vector>
#include <string>

namespace HanoiGame
{
using namespace std;

enum class Tower : int
{
TowerA,
TowerB,
TowerC,
};

class GameEngine
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;
vector<int> *tower[3];
int discs;

vector<int>& getTower(Tower tower);
public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os) const;
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(int tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game(3);
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

int iorigem = origem - 'A';
int idestino = destino - 'A';

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) iorigem, (Tower) idestino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;
this->tower[0] = &towerA;
this->tower[1] = &towerB;
this->tower[2] = &towerC;

for (size_t i = discs; i > 0; --i)
{
towerA.push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (getTower(towerO).size() == 0)
{
return false;
}

if (getTower(towerD).size() == 0)
{
return true;
}

return getTower(towerO).back() < getTower(towerD).back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = getTower(towerO).back();
getTower(towerO).pop_back();
getTower(towerD).push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return towerA.size() == 0 && (towerB.size() == discs || towerC.size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os) const
{
for (size_t i = discs; i > 0; --i)
{
os << (towerA.size() >= i && towerA[i - 1] ? std::to_string(towerA[i - 1]) : "|");

os << '\t';

os << (towerB.size() >= i && towerB[i - 1] ? std::to_string(towerB[i - 1]) : "|");

os << '\t';

os << (towerC.size() >= i && towerC[i - 1] ? std::to_string(towerC[i - 1]) : "|");

os << endl;
}
}

vector<int>& HanoiGame::GameEngine::getTower(Tower tower)
{
return *this->tower[(int)tower];
}

[/hide]

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não precisa mudar pra int, você tentou com char? Não testei, estou falando conceitualmente não deve haver problemas.

O char usei pra economizar espaço mesmo.

Economizou várias chamadas, já dá pra matar o GET e em termos de design até criar uma interface com o ponteiro cont.

Ficou bom cara.

PS: Entendi o que você fez, fiz algumas modificações, não gostei ficou feio o que eu fiz.

Agora que entendi o que você quis atingir, acho que MAP seria melhor do que o ponteiro. Não acho interessante ficar vazando conhecimento sobre a classe para fora, do tipo 'A' - tower. Se queremos ser práticos devemos usar um dicionário e não incluir esses detalhes na parte de fora da classe. Eu já fui além e coloquei do lado de dentro e não traz vantagem em economizar o GET, pois fica uma sintaxe horrorosa e usando o GET não vejo vantagem em retirar o switch por um ponteiro, estamos trocando milésimos de performance por menos compreensão imediata do código.

Acho que o MAP seria a solução ideal para indexar os valores do enum. Independente de usar 0 ou 'A' podemos continuar com o tipo char.

Fica aí minha opinião final após mexer usando esse ponteiro.

Perceba como fica mais limpo sem expor nada, nem depender de artimanhas.


#include <iostream>
#include <vector>
#include <string>
#include <map>

namespace HanoiGame
{
using namespace std;

enum class Tower : char
{
TowerA = 'A',
TowerB = 'B',
TowerC = 'C'
};

class Hanoi
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;

public:
map<char, vector<int>> tower;

Hanoi()
{
tower['A'] = towerA;
tower['B'] = towerB;
tower['C'] = towerC;
}
};

class GameEngine
{
Hanoi hanoi_tower;
int discs;

public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os);
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(char tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game{};
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) origem, (Tower) destino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;

for (size_t i = discs; i > 0; --i)
{
hanoi_tower.tower['A'].push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (hanoi_tower.tower[(char)towerO].size() == 0)
{
return false;
}

if (hanoi_tower.tower[(char)towerD].size() == 0)
{
return true;
}

return hanoi_tower.tower[(char) towerO].back() < hanoi_tower.tower[(char) towerD].back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = hanoi_tower.tower[(char) towerO].back();
hanoi_tower.tower[(char) towerO].pop_back();
hanoi_tower.tower[(char) towerD].push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return hanoi_tower.tower['A'].size() == 0 && (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os)
{
for (size_t i = discs; i > 0; --i)
{
os << (hanoi_tower.tower['A'].size() >= i && hanoi_tower.tower['A'][i - 1] ? std::to_string(hanoi_tower.tower['A'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['B'].size() >= i && hanoi_tower.tower['B'][i - 1] ? std::to_string(hanoi_tower.tower['B'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['C'].size() >= i && hanoi_tower.tower['C'][i - 1] ? std::to_string(hanoi_tower.tower['C'][i - 1]) : "|");

os << endl;
}
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Não precisa mudar pra int, você tentou com char? Não testei, estou falando conceitualmente não deve haver problemas.

O char usei pra economizar espaço mesmo.

Economizou várias chamadas, já dá pra matar o GET e em termos de design até criar uma interface com o ponteiro cont.

Ficou bom cara.

PS: Entendi o que você fez, fiz algumas modificações, não gostei ficou feio o que eu fiz.

Agora que entendi o que você quis atingir, acho que MAP seria melhor do que o ponteiro. Não acho interessante ficar vazando conhecimento sobre a classe para fora, do tipo 'A' - tower. Se queremos ser práticos devemos usar um dicionário e não incluir esses detalhes na parte de fora da classe. Eu já fui além e coloquei do lado de dentro e não traz vantagem em economizar o GET, pois fica uma sintaxe horrorosa e usando o GET não vejo vantagem em retirar o switch por um ponteiro, estamos trocando milésimos de performance por menos compreensão imediata do código.

Acho que o MAP seria a solução ideal para indexar os valores do enum. Independente de usar 0 ou 'A' podemos continuar com o tipo char.

Fica aí minha opinião final após mexer usando esse ponteiro.

Perceba como fica mais limpo sem expor nada, nem depender de artimanhas.


#include <iostream>
#include <vector>
#include <string>
#include <map>

namespace HanoiGame
{
using namespace std;

enum class Tower : char
{
TowerA = 'A',
TowerB = 'B',
TowerC = 'C'
};

class Hanoi
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;

public:
map<char, vector<int>> tower;

Hanoi()
{
tower['A'] = towerA;
tower['B'] = towerB;
tower['C'] = towerC;
}
};

class GameEngine
{
Hanoi hanoi_tower;
int discs;

public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os);
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(char tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game{};
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) origem, (Tower) destino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;

for (size_t i = discs; i > 0; --i)
{
hanoi_tower.tower['A'].push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (hanoi_tower.tower[(char)towerO].size() == 0)
{
return false;
}

if (hanoi_tower.tower[(char)towerD].size() == 0)
{
return true;
}

return hanoi_tower.tower[(char) towerO].back() < hanoi_tower.tower[(char) towerD].back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = hanoi_tower.tower[(char) towerO].back();
hanoi_tower.tower[(char) towerO].pop_back();
hanoi_tower.tower[(char) towerD].push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return hanoi_tower.tower['A'].size() == 0 && (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os)
{
for (size_t i = discs; i > 0; --i)
{
os << (hanoi_tower.tower['A'].size() >= i && hanoi_tower.tower['A'][i - 1] ? std::to_string(hanoi_tower.tower['A'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['B'].size() >= i && hanoi_tower.tower['B'][i - 1] ? std::to_string(hanoi_tower.tower['B'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['C'].size() >= i && hanoi_tower.tower['C'][i - 1] ? std::to_string(hanoi_tower.tower['C'][i - 1]) : "|");

os << endl;
}
}

Compartilhar este post


Link para o post
Compartilhar em outros sites
Não precisa mudar pra int, você tentou com char? Não testei, estou falando conceitualmente não deve haver problemas.

O char usei pra economizar espaço mesmo.

Economizou várias chamadas, já dá pra matar o GET e em termos de design até criar uma interface com o ponteiro cont.

Ficou bom cara.

PS: Entendi o que você fez, fiz algumas modificações, não gostei ficou feio o que eu fiz.

Agora que entendi o que você quis atingir, acho que MAP seria melhor do que o ponteiro. Não acho interessante ficar vazando conhecimento sobre a classe para fora, do tipo 'A' - tower. Se queremos ser práticos devemos usar um dicionário e não incluir esses detalhes na parte de fora da classe. Eu já fui além e coloquei do lado de dentro e não traz vantagem em economizar o GET, pois fica uma sintaxe horrorosa e usando o GET não vejo vantagem em retirar o switch por um ponteiro, estamos trocando milésimos de performance por menos compreensão imediata do código.

Acho que o MAP seria a solução ideal para indexar os valores do enum. Independente de usar 0 ou 'A' podemos continuar com o tipo char.

Fica aí minha opinião final após mexer usando esse ponteiro.

Perceba como fica mais limpo sem expor nada, nem depender de artimanhas.


#include <iostream>
#include <vector>
#include <string>
#include <map>

namespace HanoiGame
{
using namespace std;

enum class Tower : char
{
TowerA = 'A',
TowerB = 'B',
TowerC = 'C'
};

class Hanoi
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;

public:
map<char, vector<int>> tower;

Hanoi()
{
tower['A'] = towerA;
tower['B'] = towerB;
tower['C'] = towerC;
}
};

class GameEngine
{
Hanoi hanoi_tower;
int discs;

public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os);
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(char tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game{};
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) origem, (Tower) destino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;

for (size_t i = discs; i > 0; --i)
{
hanoi_tower.tower['A'].push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (hanoi_tower.tower[(char)towerO].size() == 0)
{
return false;
}

if (hanoi_tower.tower[(char)towerD].size() == 0)
{
return true;
}

return hanoi_tower.tower[(char) towerO].back() < hanoi_tower.tower[(char) towerD].back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = hanoi_tower.tower[(char) towerO].back();
hanoi_tower.tower[(char) towerO].pop_back();
hanoi_tower.tower[(char) towerD].push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return hanoi_tower.tower['A'].size() == 0 && (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os)
{
for (size_t i = discs; i > 0; --i)
{
os << (hanoi_tower.tower['A'].size() >= i && hanoi_tower.tower['A'][i - 1] ? std::to_string(hanoi_tower.tower['A'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['B'].size() >= i && hanoi_tower.tower['B'][i - 1] ? std::to_string(hanoi_tower.tower['B'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['C'].size() >= i && hanoi_tower.tower['C'][i - 1] ? std::to_string(hanoi_tower.tower['C'][i - 1]) : "|");

os << endl;
}
}

Realmente da pra esconomizar aquele getTower. Ainda prefiro usar o ponteiro e index baseados em 0 ao invés de Dicionario.

O metodo IsGameFinished pode ser reduzido.

bool HanoiGame::GameEngine::isGameFinished()
{
return (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

Se uma das torres tiver cheias, a A vai estar vazia e não precisaremos verificar isso.

Compartilhar este post


Link para o post
Compartilhar em outros sites
Não precisa mudar pra int, você tentou com char? Não testei, estou falando conceitualmente não deve haver problemas.

O char usei pra economizar espaço mesmo.

Economizou várias chamadas, já dá pra matar o GET e em termos de design até criar uma interface com o ponteiro cont.

Ficou bom cara.

PS: Entendi o que você fez, fiz algumas modificações, não gostei ficou feio o que eu fiz.

Agora que entendi o que você quis atingir, acho que MAP seria melhor do que o ponteiro. Não acho interessante ficar vazando conhecimento sobre a classe para fora, do tipo 'A' - tower. Se queremos ser práticos devemos usar um dicionário e não incluir esses detalhes na parte de fora da classe. Eu já fui além e coloquei do lado de dentro e não traz vantagem em economizar o GET, pois fica uma sintaxe horrorosa e usando o GET não vejo vantagem em retirar o switch por um ponteiro, estamos trocando milésimos de performance por menos compreensão imediata do código.

Acho que o MAP seria a solução ideal para indexar os valores do enum. Independente de usar 0 ou 'A' podemos continuar com o tipo char.

Fica aí minha opinião final após mexer usando esse ponteiro.

Perceba como fica mais limpo sem expor nada, nem depender de artimanhas.


#include <iostream>
#include <vector>
#include <string>
#include <map>

namespace HanoiGame
{
using namespace std;

enum class Tower : char
{
TowerA = 'A',
TowerB = 'B',
TowerC = 'C'
};

class Hanoi
{
vector<int> towerA;
vector<int> towerB;
vector<int> towerC;

public:
map<char, vector<int>> tower;

Hanoi()
{
tower['A'] = towerA;
tower['B'] = towerB;
tower['C'] = towerC;
}
};

class GameEngine
{
Hanoi hanoi_tower;
int discs;

public:
explicit GameEngine(int discs = 5);
bool moveDisc(Tower towerO, Tower towerD);
bool isValidMove(Tower towerO, Tower towerD);
bool isGameFinished();
void printGame(ostream& os);
};
}

using namespace std;
using HanoiGame::Tower;

bool checkValidTower(char tower)
{
return (tower >= 'A' && tower <= 'C');
}

void playGame()
{
HanoiGame::GameEngine game{};
char origem, destino;

do
{
game.printGame(cout);

cout << "Torre de origem:";
cin >> origem;

cout << "Torre de destino:";
cin >> destino;

origem = toupper(origem);
destino = toupper(destino);

if (!(checkValidTower(origem) && checkValidTower(destino)) || (origem == destino))
{
cout << "voce digitou uma torre invalida" << endl;
}
else
{
if (!game.moveDisc((Tower) origem, (Tower) destino))
{
cout << "Movimento Invalido!" << endl;
}
else
{
if (game.isGameFinished())
{
cout << "Parabens jogo finalizado!" << endl;
}
}
}
} while (!game.isGameFinished());

game.printGame(cout);
}

int main()
{
playGame();
}

HanoiGame::GameEngine::GameEngine(int discs)
{
this->discs = discs;

for (size_t i = discs; i > 0; --i)
{
hanoi_tower.tower['A'].push_back(i);
}
}

bool HanoiGame::GameEngine::isValidMove(Tower towerO, Tower towerD)
{
if (hanoi_tower.tower[(char)towerO].size() == 0)
{
return false;
}

if (hanoi_tower.tower[(char)towerD].size() == 0)
{
return true;
}

return hanoi_tower.tower[(char) towerO].back() < hanoi_tower.tower[(char) towerD].back();
}

bool HanoiGame::GameEngine::moveDisc(Tower towerO, Tower towerD)
{
if (isValidMove(towerO, towerD))
{
int disc = hanoi_tower.tower[(char) towerO].back();
hanoi_tower.tower[(char) towerO].pop_back();
hanoi_tower.tower[(char) towerD].push_back(disc);

return true;
}

return false;
}

bool HanoiGame::GameEngine::isGameFinished()
{
return hanoi_tower.tower['A'].size() == 0 && (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

void HanoiGame::GameEngine::printGame(ostream& os)
{
for (size_t i = discs; i > 0; --i)
{
os << (hanoi_tower.tower['A'].size() >= i && hanoi_tower.tower['A'][i - 1] ? std::to_string(hanoi_tower.tower['A'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['B'].size() >= i && hanoi_tower.tower['B'][i - 1] ? std::to_string(hanoi_tower.tower['B'][i - 1]) : "|");

os << '\t';

os << (hanoi_tower.tower['C'].size() >= i && hanoi_tower.tower['C'][i - 1] ? std::to_string(hanoi_tower.tower['C'][i - 1]) : "|");

os << endl;
}
}

Realmente da pra esconomizar aquele getTower. Ainda prefiro usar o ponteiro e index baseados em 0 ao invés de Dicionario.

O metodo IsGameFinished pode ser reduzido.

bool HanoiGame::GameEngine::isGameFinished()
{
return (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

Se uma das torres tiver cheias, a A vai estar vazia e não precisaremos verificar isso.

Compartilhar este post


Link para o post
Compartilhar em outros sites
Realmente da pra esconomizar aquele getTower. Ainda prefiro usar o ponteiro e index baseados em 0 ao invés de Dicionario.

O metodo IsGameFinished pode ser reduzido.

bool HanoiGame::GameEngine::isGameFinished()
{
return (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

Se uma das torres tiver cheias, a A vai estar vazia e não precisaremos verificar isso.

A condição foi escrita daquela forma propositalmente.

É um exemplo didático man, nós não estamos mandando um foguete para a lua. :cool:

Concordo que certas coisas explicitas são ridiculas como if (x == true), mas acho interessante ter um guard mesmo que redundante. Previne contra manutenções erradas.

Concordo que o ponteiro é mais hardcore, mas para algo simples prefiro o MAP.

Nesse caso você se safou, pois o ponteiro é apenas referência, se fosse um recurso, você lembraria de escrever o destrutor?

E escrevendo o destrutor, você lembraria de escrever o copy constructor/assignment, o move constructor/assignment?

Você percebe como a sua sugestão não é didatica?

Compartilhar este post


Link para o post
Compartilhar em outros sites
Realmente da pra esconomizar aquele getTower. Ainda prefiro usar o ponteiro e index baseados em 0 ao invés de Dicionario.

O metodo IsGameFinished pode ser reduzido.

bool HanoiGame::GameEngine::isGameFinished()
{
return (hanoi_tower.tower['B'].size() == discs || hanoi_tower.tower['C'].size() == discs);
}

Se uma das torres tiver cheias, a A vai estar vazia e não precisaremos verificar isso.

A condição foi escrita daquela forma propositalmente.

É um exemplo didático man, nós não estamos mandando um foguete para a lua. :cool:

Concordo que certas coisas explicitas são ridiculas como if (x == true), mas acho interessante ter um guard mesmo que redundante. Previne contra manutenções erradas.

Concordo que o ponteiro é mais hardcore, mas para algo simples prefiro o MAP.

Nesse caso você se safou, pois o ponteiro é apenas referência, se fosse um recurso, você lembraria de escrever o destrutor?

E escrevendo o destrutor, você lembraria de escrever o copy constructor/assignment, o move constructor/assignment?

Você percebe como a sua sugestão não é didatica?

Compartilhar este post


Link para o post
Compartilhar em outros sites
A condição foi escrita daquela forma propositalmente.

É um exemplo didático man, nós não estamos mandando um foguete para a lua. :cool:

Concordo que certas coisas explicitas são ridiculas como if (x == true), mas acho interessante ter um guard mesmo que redundante. Previne contra manutenções erradas.

Concordo que o ponteiro é mais hardcore, mas para algo simples prefiro o MAP.

Nesse caso você se safou, pois o ponteiro é apenas referência, se fosse um recurso, você lembraria de escrever o destrutor?

E escrevendo o destrutor, você lembraria de escrever o copy constructor/assignment, o move constructor/assignment?

Você percebe como a sua sugestão não é didatica?

Desconheço essas expressoes copy constructor/assignment e move constructor/assignment, porém se tivesse usado new lembraria de usar delete, ou então usaria shared_ptr.

Compartilhar este post


Link para o post
Compartilhar em outros sites
A condição foi escrita daquela forma propositalmente.

É um exemplo didático man, nós não estamos mandando um foguete para a lua. :cool:

Concordo que certas coisas explicitas são ridiculas como if (x == true), mas acho interessante ter um guard mesmo que redundante. Previne contra manutenções erradas.

Concordo que o ponteiro é mais hardcore, mas para algo simples prefiro o MAP.

Nesse caso você se safou, pois o ponteiro é apenas referência, se fosse um recurso, você lembraria de escrever o destrutor?

E escrevendo o destrutor, você lembraria de escrever o copy constructor/assignment, o move constructor/assignment?

Você percebe como a sua sugestão não é didatica?

Desconheço essas expressoes copy constructor/assignment e move constructor/assignment, porém se tivesse usado new lembraria de usar delete, ou então usaria shared_ptr.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora





Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×