Angka rahasia sudah ada; sekarang kita perlu kerangka loop untuk menampung input pengguna selama 10 ronde. Pada bagian ini kita mendefinisikan enum TebakStatus untuk merepresentasikan hasil setiap tebakan, dan fungsi game_periksa yang memulangkan enum tersebut. Loop for di main.c akan memanggil fungsi ini lalu menampilkan pesan yang sesuai.

Konsep #

Loop game yang baik punya tiga properti: batas ronde eksplisit, keluar lebih awal saat menang, dan tidak menghitung ronde untuk input invalid. Loop for (int ronde = 1; ronde <= GAME_MAKS_RONDE; ronde++) memberi batas eksplisit 10 iterasi. break mengakhiri loop saat pengguna menang. Untuk input invalid (tebakan di luar rentang), kita menurunkan ronde-- sebelum continue agar iterasi itu diabaikan.

Enum TebakStatus membuat dispatch bersih dan tipe-safe. Setiap nilai enum punya makna semantik yang jelas: TEBAK_BENAR, TEBAK_KECIL, TEBAK_BESAR, TEBAK_INVALID, TEBAK_HABIS. Pemanggil main.c memakai switch pada status tersebut untuk menentukan pesan yang ditampilkan.

Implementasi #

Enum dan prototipe fungsi di game.h:

c
typedef enum {
    TEBAK_BENAR = 0,
    TEBAK_KECIL = 1,
    TEBAK_BESAR = 2,
    TEBAK_INVALID = 3,
    TEBAK_HABIS = 4
} TebakStatus;

TebakStatus game_periksa(int tebak, int rahasia, int ronde_kini);

Implementasi game_periksa:

c
TebakStatus game_periksa(int tebak, int rahasia, int ronde_kini) {
    if (ronde_kini > GAME_MAKS_RONDE) return TEBAK_HABIS;
    if (tebak < GAME_TEBAK_MIN || tebak > GAME_TEBAK_MAKS) return TEBAK_INVALID;
    if (tebak == rahasia) return TEBAK_BENAR;
    if (tebak < rahasia)   return TEBAK_KECIL;
    return TEBAK_BESAR;
}

Loop utama di main.c:

c
for (int ronde = 1; ronde <= GAME_MAKS_RONDE; ronde++) {
    int tebak = 0;
    if (!baca_int("Tebakan Anda: ", &tebak)) {
        printf("Input tidak valid. Coba lagi.\n");
        ronde--;   /* input invalid tidak mengurangi ronde */
        continue;
    }
    TebakStatus s = game_periksa(tebak, rahasia, ronde);
    printf("Ronde %d: %s\n", ronde, status_pesan(s));
    if (s == TEBAK_INVALID) {
        ronde--;
        continue;
    }
    if (s == TEBAK_BENAR) {
        menang = 1;
        ronde_menang = ronde;
        break;
    }
}

Kode Lengkap #

Enum dan prototipe tambahan di game.h:

c
typedef enum {
    TEBAK_BENAR = 0,
    TEBAK_KECIL = 1,
    TEBAK_BESAR = 2,
    TEBAK_INVALID = 3,
    TEBAK_HABIS = 4
} TebakStatus;

TebakStatus game_periksa(int tebak, int rahasia, int ronde_kini);

Fungsi game_periksa di game.c:

c
TebakStatus game_periksa(int tebak, int rahasia, int ronde_kini) {
    if (ronde_kini > GAME_MAKS_RONDE) return TEBAK_HABIS;
    if (tebak < GAME_TEBAK_MIN || tebak > GAME_TEBAK_MAKS) return TEBAK_INVALID;
    if (tebak == rahasia) return TEBAK_BENAR;
    if (tebak < rahasia)   return TEBAK_KECIL;
    return TEBAK_BESAR;
}

Potongan loop utama di main.c (versi lengkap menyusul Bagian 5):

c
for (int ronde = 1; ronde <= GAME_MAKS_RONDE; ronde++) {
    int tebak = 0;
    if (!baca_int("Tebakan Anda: ", &tebak)) {
        printf("Input tidak valid. Coba lagi.\n");
        ronde--;
        continue;
    }
    TebakStatus s = game_periksa(tebak, rahasia, ronde);
    printf("Ronde %d: %s\n", ronde, status_pesan(s));
    if (s == TEBAK_INVALID) { ronde--; continue; }
    if (s == TEBAK_BENAR) {
        menang = 1;
        ronde_menang = ronde;
        break;
    }
}

Pengujian #

Kompilasi dan jalankan. Karena rahasia acak, output Anda akan berbeda dari contoh:

bash
gcc -std=c11 -Wall -Wextra -o /tmp/p2b2 main.c game.c
printf '50\n75\n88\n94\n97\n98\n99\n100\n95\n96\n' | /tmp/p2b2

Output yang Dihasilkan (contoh — angka rahasia berubah tiap detik):

text
=== Game Tebak Angka ===
Saya menyimpan angka antara 1 sampai 100.
Anda punya 10 ronde untuk menebak.

Tebakan Anda: Ronde 1: Tebakan terlalu besar.
Tebakan Anda: Ronde 2: Tebakan terlalu besar.
Tebakan Anda: Ronde 3: Tebakan terlalu besar.
Tebakan Anda: Ronde 4: Tebakan terlalu besar.
Tebakan Anda: Ronde 5: Tebakan terlalu besar.
Tebakan Anda: Ronde 6: Tebakan terlalu besar.
Tebakan Anda: Ronde 7: Tebakan terlalu besar.
Tebakan Anda: Ronde 8: Tebakan terlalu besar.
Tebakan Anda: Ronde 9: Tebakan terlalu besar.
Tebakan Anda: Ronde 10: Tebakan terlalu besar.

Maaf, ronde habis. Angka rahasia: 46

Mari kita telaah baris-baris kunci pada game_periksa:

  1. Baris if (ronde_kini > GAME_MAKS_RONDE) return TEBAK_HABIS;: cek batas ronde dilakukan pertama karena pemanggil mungkin saja tidak konsisten — bila ronde sudah lewat batas, kita pulangkan status yang berbeda dari INVALID agar logika di main dapat membedakan.
  2. Baris if (tebak < MIN || tebak > MAKs) return TEBAK_INVALID;: validasi rentang dilakukan sebelum perbandingan dengan rahasia. Tanpa cek ini, tebakan 0 atau 999 akan dikategorikan sebagai TEBAK_KECIL atau TEBAK_BESAR — menyesatkan pengguna.
  3. Baris if (tebak == rahasia) return TEBAK_BENAR;: pembandingan equality pada integer aman (tidak seperti floating-point). Memulangkan lebih awal menghemat branching berikutnya.
  4. Baris if (tebak < rahasia) return TEBAK_KECIL;: satu-satunya jalur yang tersisa. return TEBAK_BESAR; di akhir fungsi tidak perlu else karena return sebelumnya sudah keluar.

Bagian 3 akan memperketat validasi tebakan agar pesan error lebih informatif.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

game.c

Unduh berkas
c
/* game.c -- implementasi logika game tebak angka. */

#include "game.h"

#include <stdlib.h>
#include <time.h>

void game_init(void) {
    /* Seed generator dengan waktu saat ini. */
    srand((unsigned int)time(NULL));
}

int game_angka_rahasia(void) {
    /* rand() % N menghasilkan nilai [0, N-1]; kita geser ke rentang yang diinginkan. */
    int rentang = GAME_TEBAK_MAKS - GAME_TEBAK_MIN + 1;
    return GAME_TEBAK_MIN + (rand() % rentang);
}

TebakStatus game_periksa(int tebak, int rahasia, int ronde_kini) {
    if (ronde_kini > GAME_MAKS_RONDE) {
        return TEBAK_HABIS;
    }
    if (tebak < GAME_TEBAK_MIN || tebak > GAME_TEBAK_MAKS) {
        return TEBAK_INVALID;
    }
    if (tebak == rahasia) {
        return TEBAK_BENAR;
    }
    if (tebak < rahasia) {
        return TEBAK_KECIL;
    }
    return TEBAK_BESAR;
}

int game_hitung_skor(int ronde_menang) {
    /* Setiap ronde setelah ronde 1 mengurangi 10 poin. */
    int pengurangan = (ronde_menang - 1) * 10;
    int skor = GAME_SKOR_AWAL - pengurangan;
    if (skor < 0) skor = 0;
    return skor;
}

int game_hitung_skor_aman(int ronde_menang) {
    /* Versi yang menolak input tidak valid. */
    if (ronde_menang < 1 || ronde_menang > GAME_MAKS_RONDE) {
        return 0;
    }
    return game_hitung_skor(ronde_menang);
}

const char *game_rentang_teks(void) {
    static char buffer[32];
    snprintf(buffer, sizeof(buffer), "%d sampai %d",
             GAME_TEBAK_MIN, GAME_TEBAK_MAKS);
    return buffer;
}

main.c

Unduh berkas
c
/* main.c -- titik masuk game tebak angka.
 *
 * Memisahkan logika (game.c) dari I/O (main.c) supaya game.c dapat diuji
 * tanpa menyimulasikan keyboard.
 */

#include "game.h"

#include <stdio.h>
#include <stdlib.h>

/* Membaca satu integer dari stdin.
 * Mengembalikan 1 bila berhasil, 0 bila input tidak terurai, -1 bila EOF.
 */
static int baca_int(const char *prompt, int *out) {
    char buffer[64];
    printf("%s", prompt);
    if (fgets(buffer, sizeof(buffer), stdin) == 0) return -1;
    char *sisa = 0;
    long nilai = strtol(buffer, &sisa, 10);
    if (sisa == buffer) return 0;
    *out = (int)nilai;
    return 1;
}

static const char *status_pesan(TebakStatus s) {
    switch (s) {
        case TEBAK_BENAR:  return "Tebakan benar!";
        case TEBAK_KECIL:  return "Tebakan terlalu kecil.";
        case TEBAK_BESAR:  return "Tebakan terlalu besar.";
        case TEBAK_INVALID:return "Tebakan di luar rentang yang sah.";
        case TEBAK_HABIS:  return "Ronde sudah habis.";
        default:           return "Status tidak dikenal.";
    }
}

int main(void) {
    game_init();
    int rahasia = game_angka_rahasia();

    printf("=== Game Tebak Angka ===\n");
    printf("Saya menyimpan angka antara %s.\n", game_rentang_teks());
    printf("Anda punya %d ronde untuk menebak.\n\n", GAME_MAKS_RONDE);

    int menang = 0;
    int ronde_menang = 0;

    for (int ronde = 1; ronde <= GAME_MAKS_RONDE; ronde++) {
        int tebak = 0;
        int hasil_baca = baca_int("Tebakan Anda: ", &tebak);
        if (hasil_baca == -1) {
            printf("\nInput berakhir (EOF). Berhenti.\n");
            break;
        }
        if (hasil_baca == 0) {
            printf("Input tidak valid. Coba lagi.\n");
            ronde--;   /* jangan pakai ronde untuk input invalid */
            continue;
        }

        TebakStatus s = game_periksa(tebak, rahasia, ronde);
        printf("Ronde %d: %s\n", ronde, status_pesan(s));

        if (s == TEBAK_INVALID) {
            ronde--;   /* input invalid tidak mengurangi ronde */
            continue;
        }
        if (s == TEBAK_BENAR) {
            menang = 1;
            ronde_menang = ronde;
            break;
        }
    }

    if (menang) {
        int skor = game_hitung_skor_aman(ronde_menang);
        printf("\nSelamat! Angka rahasia: %d\n", rahasia);
        printf("Anda menang di ronde %d dengan skor %d.\n", ronde_menang, skor);
    } else {
        printf("\nMaaf, ronde habis. Angka rahasia: %d\n", rahasia);
    }
    return 0;
}

tests.c

Unduh berkas
c
/* tests.c -- rangkaian test untuk modul game. */

#include "game.h"

#include <stdio.h>

static int lulus = 0;
static int gagal = 0;

#define PERIKSA(kondisi, nama)                              \
    do {                                                    \
        if (kondisi) { lulus++; printf("[LULUS] %s\n", nama); } \
        else { gagal++; printf("[GAGAL] %s\n", nama); }     \
    } while (0)

static void test_rentang_rahasia(void) {
    /* Karena angka acak, kita tes sebanyak 1000 kali untuk memastikan
     * seluruh hasil berada dalam rentang sah.
     */
    game_init();
    int semua_sah = 1;
    for (int i = 0; i < 1000; i++) {
        int r = game_angka_rahasia();
        if (r < GAME_TEBAK_MIN || r > GAME_TEBAK_MAKS) {
            semua_sah = 0;
            break;
        }
    }
    PERIKSA(semua_sah, "Angka rahasia selalu di rentang sah");
}

static void test_periksa_benar(void) {
    TebakStatus s = game_periksa(50, 50, 1);
    PERIKSA(s == TEBAK_BENAR, "Tebakan 50 dengan rahasia 50 -> BENAR");
}

static void test_periksa_kecil(void) {
    TebakStatus s = game_periksa(30, 50, 1);
    PERIKSA(s == TEBAK_KECIL, "Tebakan 30 dengan rahasia 50 -> KECIL");
}

static void test_periksa_besar(void) {
    TebakStatus s = game_periksa(70, 50, 1);
    PERIKSA(s == TEBAK_BESAR, "Tebakan 70 dengan rahasia 50 -> BESAR");
}

static void test_periksa_invalid(void) {
    TebakStatus s = game_periksa(0, 50, 1);
    PERIKSA(s == TEBAK_INVALID, "Tebakan 0 di luar rentang -> INVALID");
}

static void test_periksa_habis(void) {
    TebakStatus s = game_periksa(50, 50, GAME_MAKS_RONDE + 1);
    PERIKSA(s == TEBAK_HABIS, "Ronde > MAKs -> HABIS");
}

static void test_skor_ronde1(void) {
    int skor = game_hitung_skor_aman(1);
    PERIKSA(skor == GAME_SKOR_AWAL, "Menang ronde 1 -> skor penuh");
}

static void test_skor_ronde5(void) {
    int skor = game_hitung_skor_aman(5);
    PERIKSA(skor == 60, "Menang ronde 5 -> skor 60");
}

static void test_skor_invalid(void) {
    int skor = game_hitung_skor_aman(0);
    PERIKSA(skor == 0, "Ronde 0 -> skor 0");
    skor = game_hitung_skor_aman(GAME_MAKS_RONDE + 1);
    PERIKSA(skor == 0, "Ronde > MAKs -> skor 0");
}

int main(void) {
    test_rentang_rahasia();
    test_periksa_benar();
    test_periksa_kecil();
    test_periksa_besar();
    test_periksa_invalid();
    test_periksa_habis();
    test_skor_ronde1();
    test_skor_ronde5();
    test_skor_invalid();
    printf("\nRingkasan: %d lulus, %d gagal\n", lulus, gagal);
    return gagal == 0 ? 0 : 1;
}