Files
uno/uno.cpp
T

231 lines
7.2 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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;
}