Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 67 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,68 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/b842RB8g)
# Tugas Besar 1 IF2010 Pemrograman Berorientasi Objek
# Nimonspoli - CLI Monopoly Game in C++

Project Structure ini hanyalah referensi, Anda dapat menyesuaikannya dengan kebutuhan tim Anda
## 🎲 Deskripsi
**Nimonspoli** adalah permainan papan legendaris Monopoly yang diadaptasi sepenuhnya ke dalam platform *Command Line Interface* (CLI). Dibangun menggunakan bahasa pemrograman **C++17** dengan fondasi *Object-Oriented Programming* (OOP) yang kokoh (mengadopsi prinsip *Tell, Don't Ask* dan *Runtime Polymorphism*).

Permainan ini hadir dengan fitur papan visual (*hollow square* 11x11), sistem data dinamis yang membaca konfigurasi dari berkas eksternal (`property.txt`), manipulasi *string* `iomanip` yang rapi, serta *state-management* persisten selama sesi berlangsung.

---

## ⚙️ Persyaratan Sistem (Requirements)
- **Compiler:** Mendukung C++17 standar (misal: GCC 7.3+, Clang 5.0+).
- **Build Tool:** GNU Make.
- **Terminal:** Direkomendasikan menggunakan *Terminal Emulator* modern yang sepenuhnya mendukung sistem warna dan formating *ANSI Escape Codes* (WSL, Linux Terminal, macOS Terminal, atau Windows Terminal).

---

## 🛠️ Cara Build & Kompilasi (Compile)

Proyek ini menggunakan `Makefile` untuk manajemen modul kompilasi yang terotomatisasi (meliputi layer `core`, `models`, `views`, dan `utils`).

1. Buka *terminal* Anda.
2. Pastikan direktori aktif Anda (*Current Working Directory*) berada tepat di folder akar (*root*) proyek Nimonspoli.
3. Jalankan instruksi berikut untuk memicu kompilasi penuh:

```bash
make
```

> **Catatan:** Jika *build* sukses, file biner *executable* akan secara otomatis digenerasikan dan diletakkan pada path `./bin/game`. Semua *object files* akan diletakkan di dalam folder `./build/`.

---

## 🚀 Cara Menjalankan (Run)

Sangat penting untuk mengeksekusi program dari *root directory* dari proyek ini agar sistem *ConfigParser* berhasil melacak *path* konfigurasi file `data/property.txt` dengan benar.

Anda bisa menjalankannya via `Makefile`:
```bash
make run
```

Atau jika ingin mengeksekusi biner utamanya secara manual:
```bash
./bin/game
```

---

## 🕹️ Daftar Perintah CLI (Command List)

Saat permainan dimulai, Anda akan diminta untuk memasukkan jumlah pemain dan nama setiap pemain. Selama giliran aktif (selama Anda belum menyentuh kondisi *End Turn*), Anda bebas mengeksekusi berbagai instruksi ini:

| Perintah | Deskripsi |
| :--- | :--- |
| `CETAK_PAPAN` | Mencetak visual 11x11 *board* Nimonspoli beserta letak bidak `(1)(2)...` dan tingkat bangunan rumah/hotel `(^)` yang presisi. |
| `LEMPAR_DADU` | Mengocok sepasang dadu (D6) secara acak. <br>*💡 Hint: Anda cukup menekan tombol `Enter` (kosong) sebagai shortcut pelemparan dadu.* |
| `ATUR_DADU X Y` | Memanipulasi angka sepasang dadu. Sangat berguna untuk kepentingan *Testing* dan proses penilaian asisten (contoh: `ATUR_DADU 1 4`). Pastikan `X` dan `Y` bernilai lebih dari 0. |
| `CETAK_AKTA` | Melihat informasi dan UI Mini-Akta lengkap milik sebuah properti (mencakup harga sewa bertingkat, kepemilikan, harga beli/gadai). Anda akan diminta memasukkan "KODE" singkat properti (contoh: `GBR`, `JKT`, `PLN`). |
| `HELP` | Menampilkan panduan bantuan ringkas tentang seluruh perintah di atas selama *runtime*. |

---

## 🌟 Fitur Inti Mekanik

- **Auto-Rent Enforcement:** Berkat implementasi prinsip "*Tell, Don't Ask*", pemain yang mendarat di properti musuh akan langsung ditarik saldo sewanya secara sistematis.
- **Auto-Acquisition:** Membeli petak jalan biasa (`Street`) akan memunculkan *prompt* `(Y/N)`, tetapi mendarat di Stasiun (`Railroad`) atau Perusahaan (`Utility`) kosong akan langsung diberikan secara gratis (Otomatis).
- **Rule of Threes (Penjara):** Mendapatkan lemparan dadu kembar (Double) 3 kali berturut-turut akan dianggap ngebut dan pemain dijebloskan langsung ke JAIL.
- **Anti-Crash Parsing:** Validasi input *commands* telah dirancang sangat *robust*. Kesalahan pengetikan, kelebihan spasi, atau input di luar batasan tidak akan menyebabkan program *Segmentation Fault* berkat manajemen proteksi tipe dinamis di C++17.
29 changes: 29 additions & 0 deletions data/property.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
ID KODE NAMA JENIS WARNA HARGA_LAHAN NILAI_GADAI UPG_RUMAH UPG_HT RENT_L0…RENT_L5
2 GRT GARUT STREET COKLAT 60 40 20 50 2 … 250
4 TSK TASIKMALAYA STREET COKLAT 60 40 50 50 4 … 320
6 GBR STASIUN_GAMBIR RAILROAD DEFAULT 0 20
7 BGR BOGOR STREET BIRU_MUDA 100 80 20 50 6 … 400
9 DPK DEPOK STREET BIRU_MUDA 100 80 20 50 6 … 400
10 BKS BEKASI STREET BIRU_MUDA 120 90 20 50 8 … 450
12 MGL MAGELANG STREET MERAH_MUDA 140 100 100 100 10 … 750
13 PLN PLN UTILITY ABU_ABU 0 25
14 SOL SOLO STREET MERAH_MUDA 140 100 100 100 10 50 … 750
15 YOG YOGYAKARTA STREET MERAH_MUDA 160 120 100 100 12 … 900
16 STB STASIUN_BANDUNG RAILROAD DEFAULT 0 40
17 MAL MALANG STREET ORANGE 180 135 100 100 14 … 950
19 SMG SEMARANG STREET ORANGE 180 140 100 100 14 … 950
20 SBY SURABAYA STREET ORANGE 200 150 100 100 16 … 1000
22 MKS MAKASSAR STREET MERAH 220 175 150 150 18 … 1050
24 BLP BALIKPAPAN STREET MERAH 220 175 150 150 18 … 1050
25 MND MANADO STREET MERAH 240 190 150 150 20 … 1100
26 TUG STASIUN_TUGU RAILROAD DEFAULT 0 50
27 PLB PALEMBANG STREET KUNING 260 200 150 150 22 … 1150
28 PKB PEKANBARU STREET KUNING 260 210 150 150 22 … 1150
29 PAM PAM UTILITY ABU_ABU 0 60
30 MED MEDAN STREET KUNING 280 225 150 150 24 … 1200
32 BDG BANDUNG STREET HIJAU 300 250 200 200 26 … 1275
33 DEN DENPASAR STREET HIJAU 300 260 200 200 26 … 1275
35 MTR MATARAM STREET HIJAU 320 280 200 200 28 … 1400
36 GUB STASIUN_GUBENG RAILROAD DEFAULT 0 120
38 JKT JAKARTA STREET BIRU_TUA 350 300 200 200 35 … 1500
40 IKN IBU_KOTA_NUSANTARA STREET BIRU_TUA 400 350 200 200 50 … 2000
42 changes: 42 additions & 0 deletions include/core/GameManager.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef GAMEMANAGER_HPP
#define GAMEMANAGER_HPP

#include <vector>
#include <string>
#include <memory>

#include "models/Player.hpp"
#include "models/Petak.hpp"
#include "models/Dice.hpp"
#include "models/CardDeck.hpp"

class PetakProperti;

class GameManager {
private:
std::vector<Player> players;
std::vector<std::unique_ptr<Petak>> board;
Dice dice;
CardDeck chanceDeck;
CardDeck chestDeck;

int currentPlayerIndex;
int turnCount;

// Helper method for handling declined properties
void startAuction(PetakProperti* prop);
void executePostRoll(Player& p, int roll1, int roll2, int& doublesCount, bool& endTurnFlag);
void cetakAktaCommand();

public:
GameManager();

void loadConfig(const std::string& filename);
void initPlayers(int numPlayers);
void playTurn();
void nextTurn();
Player* checkWinner();
void startGame();
};

#endif
32 changes: 32 additions & 0 deletions include/models/Card.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef CARD_HPP
#define CARD_HPP

#include <string>

class Player;

enum class CardEffectType {
ADD_MONEY,
DEDUCT_MONEY,
MOVE_TO,
GO_TO_JAIL
};

class Card {
protected:
std::string description;
CardEffectType effectType;
int value; // Money amount or board index

public:
Card(const std::string& desc, CardEffectType type, int value);
virtual ~Card() = default;

std::string getDescription() const;
CardEffectType getEffectType() const;
int getValue() const;

virtual void applyEffect(Player& p);
};

#endif
25 changes: 25 additions & 0 deletions include/models/CardDeck.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef CARDDECK_HPP
#define CARDDECK_HPP

#include "models/Card.hpp"
#include <vector>
#include <string>

class CardDeck {
private:
std::string deckName;
std::vector<Card> cards;

public:
CardDeck(const std::string& name);

std::string getName() const;
void addCard(const Card& card);

void shuffleDeck();
Card drawCard();

bool isEmpty() const;
};

#endif
25 changes: 25 additions & 0 deletions include/models/Dice.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef DICE_HPP
#define DICE_HPP

#include <utility>
#include <random>

class Dice {
private:
int lastRoll1;
int lastRoll2;
std::mt19937 generator;

public:
Dice();

int roll();

int getTotal() const;

bool isDouble() const;

std::pair<int, int> getLastRoll() const;
};

#endif
31 changes: 31 additions & 0 deletions include/models/Petak.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef PETAK_HPP
#define PETAK_HPP

#include <string>

class Player;

class Petak {
protected:
int id;
std::string name;
std::string shortName;
std::string colorCode;

public:
Petak(int id, const std::string& name);
virtual ~Petak() = default;

int getId() const;
std::string getName() const;

// Display helpers
void setSkin(const std::string& sName, const std::string& cCode);
std::string getShortName() const;
std::string getColorCode() const;

// Pure virtual method inherited by all types of Petak
virtual void injak(Player& p) = 0;
};

#endif
27 changes: 27 additions & 0 deletions include/models/PetakAksi.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef PETAKAKSI_HPP
#define PETAKAKSI_HPP

#include "models/Petak.hpp"

enum class ActionType {
GO,
JAIL,
TAX,
FREE_PARKING
};

class PetakAksi : public Petak {
private:
ActionType type;
int amount; // Used for GO bonus or TAX penalty

public:
PetakAksi(int id, const std::string& name, ActionType type, int amount = 0);

ActionType getType() const;
int getAmount() const;

void injak(Player& p) override;
};

#endif
31 changes: 31 additions & 0 deletions include/models/PetakProperti.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef PETAKPROPERTI_HPP
#define PETAKPROPERTI_HPP

#include "models/Petak.hpp"

class Player;

class PetakProperti : public Petak {
protected:
Player* owner;
int hargaBeli;
bool isMortgaged;

public:
PetakProperti(int id, const std::string& name, int hargaBeli);
virtual ~PetakProperti() = default;

Player* getOwner() const;
void setOwner(Player* newOwner);

int getHargaBeli() const;

bool getIsMortgaged() const;
void setMortgaged(bool status);

virtual int getSewa(int diceRoll = 0) const = 0;

void injak(Player& p) override;
};

#endif
48 changes: 48 additions & 0 deletions include/models/Player.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef PLAYER_HPP
#define PLAYER_HPP

#include <string>
#include <vector>
#include <memory>

class PetakProperti;

enum class PlayerStatus {
ACTIVE,
IN_JAIL,
BANKRUPT
};

class Player {
private:
int id;
std::string name;
int money;
int position;
PlayerStatus status;

std::vector<PetakProperti*> ownedProperties;

public:
Player(int id, const std::string& name, int startingMoney = 1500);
~Player() = default;

int getId() const;
std::string getName() const;

int getMoney() const;
void addMoney(int amount);
void reduceMoney(int amount);

int getPosition() const;
void setPosition(int pos);
void move(int steps, int boardSize = 40);

PlayerStatus getStatus() const;
void setStatus(PlayerStatus newStatus);

void addProperty(PetakProperti* property);
const std::vector<PetakProperti*>& getOwnedProperties() const;
};

#endif
16 changes: 16 additions & 0 deletions include/models/Railroad.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef RAILROAD_HPP
#define RAILROAD_HPP

#include "models/PetakProperti.hpp"

class Railroad : public PetakProperti {
private:
int baseRent;

public:
Railroad(int id, const std::string& name, int hargaBeli, int baseRent);

int getSewa(int diceRoll = 0) const override;
};

#endif
Loading