231 lines
7.2 KiB
C++
231 lines
7.2 KiB
C++
#include <iostream>
|
||
#include <vector>
|
||
#include <string>
|
||
#include <random>
|
||
#include <algorithm>
|
||
#include <limits>
|
||
#include <fmt/core.h>
|
||
#include <fmt/color.h>
|
||
|
||
enum class Color { Red, Green, Blue, Yellow };
|
||
|
||
std::string colorToString(Color c) {
|
||
switch (c) {
|
||
case Color::Red: return "Red";
|
||
case Color::Green: return "Green";
|
||
case Color::Blue: return "Blue";
|
||
case Color::Yellow: return "Yellow";
|
||
default: return "Unknown";
|
||
}
|
||
}
|
||
|
||
struct Card {
|
||
Color color;
|
||
int number;
|
||
|
||
void print() const {
|
||
fmt::color fmt_color;
|
||
switch (color) {
|
||
case Color::Red: fmt_color = fmt::color::red; break;
|
||
case Color::Green: fmt_color = fmt::color::green; break;
|
||
case Color::Blue: fmt_color = fmt::color::blue; break;
|
||
case Color::Yellow: fmt_color = fmt::color::yellow; break;
|
||
}
|
||
fmt::print(fmt::fg(fmt_color), "[{} {}] ", colorToString(color), number);
|
||
}
|
||
|
||
bool matches(const Card& other) const {
|
||
return color == other.color || number == other.number;
|
||
}
|
||
};
|
||
|
||
std::vector<Card> createDeck() {
|
||
std::vector<Card> deck;
|
||
for (int c = 0; c < 4; ++c) {
|
||
for (int n = 0; n <= 9; ++n) {
|
||
// Two of each card (except 0 which appears once)
|
||
deck.push_back({ static_cast<Color>(c), n });
|
||
if (n != 0) deck.push_back({ static_cast<Color>(c), n });
|
||
}
|
||
}
|
||
std::random_device rd;
|
||
std::mt19937 g(rd()); // Mersenne Twister random number generator
|
||
|
||
for (int i = 0; i < 10; ++i) {
|
||
std::shuffle(deck.begin(), deck.end(), g);
|
||
}
|
||
return deck;
|
||
}
|
||
|
||
Card drawCard(std::vector<Card>& deck) {
|
||
if (deck.empty()) throw std::runtime_error("Deck is empty!");
|
||
Card c = deck.back();
|
||
deck.pop_back();
|
||
return c;
|
||
}
|
||
|
||
void printHand(const std::vector<Card>& hand) {
|
||
for (size_t i = 0; i < hand.size(); ++i) {
|
||
fmt::print("[{}] ", i);
|
||
hand[i].print();
|
||
fmt::print("\n");
|
||
}
|
||
}
|
||
|
||
bool hasPlayableCard(const std::vector<Card>& hand, const Card& topCard) {
|
||
for (const auto& card : hand) {
|
||
if (card.matches(topCard)) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void clearScreen() {
|
||
#ifdef _WIN32
|
||
std::system("cls");
|
||
#else
|
||
std::system("clear");
|
||
#endif
|
||
}
|
||
|
||
std::string rightAlign(std::string text, size_t width = 50) {
|
||
if (text.length() >= width) return text;
|
||
return std::string(width - text.length(), ' ') + text;
|
||
}
|
||
|
||
int main() {
|
||
auto deck = createDeck();
|
||
std::vector<Card> playerHand, aiHand;
|
||
|
||
for (int i = 0; i < 7; ++i) {
|
||
playerHand.push_back(drawCard(deck));
|
||
aiHand.push_back(drawCard(deck));
|
||
}
|
||
|
||
Card topCard = drawCard(deck);
|
||
bool playerTurn = true;
|
||
|
||
while (true) {
|
||
clearScreen();
|
||
|
||
// Prepare UNO! labels if needed
|
||
std::string playerUno = (playerHand.size() == 1) ? rightAlign(fmt::format(fmt::emphasis::bold, "YOU: UNO!")) : "";
|
||
std::string aiUno = (aiHand.size() == 1) ? rightAlign(fmt::format(fmt::emphasis::bold, "AI: UNO!")) : "";
|
||
|
||
// Print top card and UNO! indicator
|
||
fmt::print("Top card: ");
|
||
topCard.print();
|
||
if (!playerUno.empty()) fmt::print("{}", playerUno);
|
||
if (!aiUno.empty()) fmt::print("{}", aiUno);
|
||
fmt::print("\n");
|
||
|
||
fmt::print("AI has {} card(s).\n\n", aiHand.size());
|
||
|
||
if (playerHand.empty()) {
|
||
fmt::print(fmt::fg(fmt::color::green), "You win!\n");
|
||
break;
|
||
}
|
||
|
||
if (aiHand.empty()) {
|
||
fmt::print(fmt::fg(fmt::color::red), "AI wins!\n");
|
||
break;
|
||
}
|
||
|
||
if (deck.empty()) {
|
||
fmt::print(fmt::fg(fmt::color::gray), "Deck is empty! Reshuffling...\n");
|
||
deck = createDeck(); // not perfect, doesn’t exclude cards in play
|
||
}
|
||
|
||
if (playerTurn) {
|
||
fmt::print(fmt::fg(fmt::color::cyan), "Your turn:\n");
|
||
printHand(playerHand);
|
||
|
||
if (!hasPlayableCard(playerHand, topCard)) {
|
||
fmt::print("No playable card. Press 'd' to draw: ");
|
||
char input;
|
||
std::cin >> input;
|
||
if (input == 'd' || input == 'D') {
|
||
Card drawn = drawCard(deck);
|
||
playerHand.push_back(drawn);
|
||
fmt::print("You drew: ");
|
||
drawn.print();
|
||
fmt::print("\nPress Enter to continue...");
|
||
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||
std::cin.get();
|
||
std::cin.putback('\n');
|
||
playerTurn = false;
|
||
continue;
|
||
}
|
||
}
|
||
else {
|
||
while (true) {
|
||
fmt::print("Choose card index to play or 'd' to draw: ");
|
||
std::string input;
|
||
std::cin >> input;
|
||
|
||
if (input == "d" || input == "D") {
|
||
Card drawn = drawCard(deck);
|
||
playerHand.push_back(drawn);
|
||
fmt::print("You drew: ");
|
||
drawn.print();
|
||
fmt::print("\nPress Enter to continue...");
|
||
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||
std::cin.get();
|
||
std::cin.putback('\n');
|
||
playerTurn = false;
|
||
break;
|
||
}
|
||
|
||
try {
|
||
int choice = std::stoi(input);
|
||
if (choice >= 0 && choice < static_cast<int>(playerHand.size())) {
|
||
if (playerHand[choice].matches(topCard)) {
|
||
topCard = playerHand[choice];
|
||
playerHand.erase(playerHand.begin() + choice);
|
||
playerTurn = false;
|
||
break;
|
||
}
|
||
else {
|
||
fmt::print("Invalid move. You must match color or number.\n");
|
||
}
|
||
}
|
||
else {
|
||
fmt::print("Invalid index.\n");
|
||
}
|
||
}
|
||
catch (...) {
|
||
fmt::print("Invalid input.\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
fmt::print(fmt::fg(fmt::color::yellow), "AI's turn...\n");
|
||
bool played = false;
|
||
for (size_t i = 0; i < aiHand.size(); ++i) {
|
||
if (aiHand[i].matches(topCard)) {
|
||
topCard = aiHand[i];
|
||
aiHand.erase(aiHand.begin() + i);
|
||
fmt::print("AI played: ");
|
||
topCard.print();
|
||
fmt::print("\n");
|
||
played = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!played) {
|
||
aiHand.push_back(drawCard(deck));
|
||
fmt::print("AI drew a card.\n");
|
||
}
|
||
|
||
fmt::print("Press Enter to continue...");
|
||
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||
std::cin.get();
|
||
|
||
playerTurn = true;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|