Membuat Permainan Congklak dengan Bahasa C (Bagian 1)

Congklak, atau dikenal juga dengan nama dhakon atau mancala, adalah sebuah permainan tradisional di Asia Tenggara. Permainan ini dimainkan oleh dua orang. Pada papan congklak terdapat tujuh lubang kecil (lumbung) yang saling berhadapan dan satu buah lubang besar (rumah) masing-masing di sisi kiri dan kanan. Masing-masing lubang kecil ini berisi tujuh buah biji-bijian atau kerang. Tujuan utama yang harus dicapai untuk memenangkan permainan adalah dengan memindahkan seluruh biji ke rumah di sebelah kanan pemain. Pemain yang berhasil mengisi lubang besar terbanyak adalah pemenangnya.

Mari kita coba buat simulasi permainan tersebut dengan menggunakan bahasa pemrograman C. Seri tutorial pemrograman ini akan dibagi ke dalam 3 bagian, di mana bagian pertama akan membahas cara pencetakan (rendering) papan permainan, dan animasi pergerakan biji.

Bagian berikutnya akan membahas mengenai alur permainan dan penentuan pemenang, sedangkan pada bagian ketiga kita akan mencoba membuat pemain komputer agar permainan bisa dimainkan oleh satu orang melawan komputer.

Menyiapkan papan congklak dan lubang-lubangnya

Pertama-tama, mari siapkan boilerplate program, dengan meng-include pustaka-pustaka berikut:

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

/**
 * Flush input buffer
 */
void flush_input() {
  int c;
  while ((c = getchar()) != '\n' && c != EOF);
}

int main(int argc, char **argv)
{
  return 0;
}

Seperti yang kita ketahui, secara keseluruhan ada 16 lubang pada congklak. Untuk itu kita siapkan sebuah array yang merepresentasikan lubang untuk menampung banyaknya biji-bijian pada masing-masing lubang tersebut. Variabel ini didefinisikan secara global (didefinisikan diluar main()) agar dapat diakses dari seluruh bagian program.

// Ada 16 lubang pada permainan congklak, dengan susunan sbb:
// 0..6  lubang kecil pemain 1
// 7     lubang besar pemain 1
// 8..14 lubang kecil pemain 2
// 15    lubang besar pemain 2
int lubang[16];

Dari ke-16 lubang tersebut, mari kita tentukan posisi dari masing-masing elemen array pada tiap lubang, mulai dari sisi kiri bawah berputar melawan arah jarum jam sebagai berikut. Posisi ini penting diingat karena kita akan menggunakan posisi ini sebagai referensi pada seluruh bagian dari program.

141312111098
157
0123456

Berdasarkan posisi di atas, dapat disimpulkan kepemilikan tiap lubang sebagai berikut.

  • lubang[0] s.d. lubang[6] merupakan lubang kecil (lumbung) milik pemain 1
  • lubang[7] merupakan lubang besar (rumah) milik pemain 1
  • lubang[8] s.d. lubang[14] merupakan lubang kecil (lumbung) milik pemain 2
  • lubang[15] merupakan lubang besar (rumah) milik pemain 2

Reset papan congklak

Setelah menyiapkan lubangnya, mari kita isi lubang-lubang kecil dengan masing-masing 7 biji, dan kosongkan rumah. Kita tuliskan proses ini dalam sebuah fungsi reset_papan().

/**
 * Mereset lubang dengan 7 biji pada masing-masing lubang kecuali 
 * lubang besar (rumah).
 */
void reset_papan() {
  // Isi 7 biji pada lubang kecil pemain 1
  for (int i = 0; i <= 6; i++) {
    lubang[i] = 7;
  }
  
  // Kosongkan rumah pemain 1
  lubang[7] = 0;
  
  // Isi 7 biji pada lubang kecil pemain 2
  for (int i = 8; i <= 14; i++) {
    lubang[i] = 7;
  }
  
  // Kosongkan lubang besar pemain 2
  lubang[15] = 0;
}

Menampilkan papan congklak di layar

Sekarang mari kita buat kode program untuk menampilkan papan congklak di layar. Papan congklak ini bisa dicetak dalam tiga baris seperti ini.

/**
 * Bersihkan layar dan cetak papan congklak.
 */
void cetak_papan() {
  // Contoh papan dan index masing-masing lubang
  // ______A____B____C____D____E____F____G_____
  //
  //     (14) (13) (12) (11) (10) ( 9) ( 8)    
  // (15)                                  ( 7)
  //     ( 0) ( 1) ( 2) ( 3) ( 4) ( 5) ( 6)
  
  system("clear");
  
  printf("______A____B____C____D____E____F____G_____\n\n");
  
  // Lubang kecil di atas
  printf("    ");
  for (int i = 14; i >= 8; i--) {
    printf("(%2d) ", lubang[i]);
  }
  printf("\n");
  
  // Lubang besar di kiri dan kanan
  printf("(%2d)                                  (%2d)\n", lubang[15], lubang[7]);
  
  // Lubang kecil di bawah
  printf("    ");
  for (int i = 0; i <= 6; i++) {
    printf("(%2d) ", lubang[i]);
  }
  printf("\n\n");
}

Sebelum mencetak, kita bersihkan layar terlebih dulu menggunakan system("clear").

MS Windows: Jika kamu membuat program di Windows, ganti perintah system("clear") menjadi system("cls").

Pada kode di atas aku juga memberi tanda huruf A sampai G di atas tiap lubang untuk memudahkan.

Sekarang mari kita coba reset dan cetak papan di fungsi program utama.

int main(int argc, char **argv)
{
  reset_papan();
  cetak_papan();
  getchar();
  return 0;
}

Coba compile dan jalankan program untuk melihat hasilnya.

Membuat simulasi aksi-aksi dalam permainan

Berdasarkan aturan permainan congklak, ada beberapa aksi yang dapat dilakukan oleh pemain:

  1. Mengambil semua biji di lubang terpilih dan mendistribusikannya satu per satu berlawanan arah jarum jam.
  2. Mengambil semua biji di lubang yang berseberangan dan menaruh semuanya ke rumah. Aksi ini dikenal juga dengan sebutan “menembak” lumbung.

Bergantung pada giliran dan posisi biji terakhir yang ditaruh, pemain bisa melakukan salah satu dari dua aksi di atas sebelum gilirannya berakhir.

Mengambil dan mendistribusikan biji

Mari kita buat simulasi aksi yang pertama terlebih dulu. Setiap kali ada pergerakan (perubahan state), kita lakukan pencetakan ulang papan dan beri jeda waktu 500 milidetik. Hal ini agar animasi pendistribusian biji terlihat oleh pemain.

/**
 * Mengambil isi lubang dan mendistribusikan biji ke lubang di 
 * sebelahnya berlawanan jarum jam hingga habis.
 * 
 * @param index Indeks lubang yang akan diambil dan didistribusikan.
 * @param pemain Nomor urut pemain
 * @return Indeks lubang di mana biji terakhir ditaruh.
 */
int distribusi_biji(int index, int pemain) {
  // Pindahkan biji ke genggaman
  int genggaman = lubang[index];
  lubang[index] = 0;
  
  // Perbaharui papan dan beri jeda waktu sebelum melanjutkan
  cetak_papan();
  printf("[%2d]\n", genggaman);
  usleep(500000);
  
  // Distribusikan biji berlawanan arah jarum jam
  while (genggaman > 0) {
    // Geser index ke lubang di sampingnya
    index = (index + 1) % 16;
    
    // Jika index berada di lubang besar pemain lawan, lompati lubang tersebut
    if (((pemain == 1) && (index == 15)) || ((pemain == 2) && (index == 7))) {
      index = (index + 1) % 16;
    }
    
    // Pindahkan satu biji dari genggaman ke lubang
    lubang[index]++;
    genggaman--;

    // Perbaharui papan dan beri jeda waktu sebelum melanjutkan
    cetak_papan();
    printf("[%2d]\n", genggaman);
    usleep(500000);
  }
  
  return index;
}

Fungsi ini menerima input parameter berupa indeks dari lubang yang akan diambil dan nomor urut pemain. Setelah fungsi selesai, maka fungsi mengembalikan nilai indeks dari lubang di mana biji terakhir ditaruh.

MS Windows: Jika kamu membuat program di Windows, ganti perintah usleep(500000) menjadi Sleep(500) (dengan huruf S besar). Selain itu, ubah #include <unistd.h> menjadi #include <windows.h>.

Salah satu hal penting dari proses distribusi ini adalah biji akan diletakkan di setiap lubang kecuali rumah milik pemain lawan. Itulah sebabnya saat index berada di lubang 15 (jika pemain 1) atau lubang 7 (jika pemain 2), index akan digeser ke sebelahnya.

Coba dulu apakah simulasi tersebut berfungsi dengan mengambil di lubang A (indeks 0). Jika program berjalan dengan benar, maka biji di tiap lumbung sisi pemain 1 akan bertambah menjadi 8 dan rumah terisi 1 biji.

int main(int argc, char **argv)
{
  // Nomor urut giliran pemain, dimulai dari pemain 1
  int pemain = 1;

  reset_papan();
  cetak_papan();
  getchar();
  
  distribusi_biji(0, pemain);
  getchar();
  return 0;
}

Menembak lumbung

Dibandingkan aksi distribusi di atas, aksi ini cukup sederhana karena kita hanya perlu memindahkan seluruh biji dari satu lubang ke lubang lain. Karena ada dua buah rumah, kita tambahkan parameter untuk menentukan ke rumah milik pemain manakan biji tersebut akan dipindahkan.

/**
 * Mengambil isi lubang dan memindahkan semuanya ke rumah milik pemain.
 * 
 * @param index Indeks lubang yang akan diambil.
 * @param pemain Nomor urut pemain.
 */
void menembak_biji(int index, int pemain) {
  // Pindahkan biji ke genggaman
  int genggaman = lubang[index];
  lubang[index] = 0;
  
  // Perbaharui papan dan beri jeda waktu sebelum melanjutkan
  cetak_papan();
  cetak_genggaman(index, genggaman);
  usleep(500000);
  
  // Indeks rumah milik pemain:
  // - pemain 1 = lubang[7]
  // - pemain 2 = lubang[15]
  if (pemain == 1) {
    index = 7;
  } else {
    index = 15;
  }
  lubang[index] += genggaman;
  genggaman = 0;

  // Perbaharui papan dan beri jeda waktu sebelum melanjutkan
  cetak_papan();
  cetak_genggaman(index, genggaman);
  usleep(500000);
}

Sekarang coba lagi dengan mengambil biji di lubang D di atas (indeks 11) ke milik pemain 1. Jika program berjalan dengan benar, maka lubang besar pemain 1 bertambah menjadi 8.

int main(int argc, char **argv)
{
  // Nomor urut giliran pemain, dimulai dari pemain 1
  int pemain = 1;

  reset_papan();
  cetak_papan();
  getchar();
  
  distribusi_biji(0, pemain);
  getchar();
  
  menembak_biji(11, pemain);
  getchar();
  return 0;
}

Hingga tahap ini, kamu bisa coba mengubah-ubah urutan atau menambahkan perintah distribusi_biji() dan pindahkan_biji() pada fungsi program utama. Aku akan membahas alur permainan pada bagian artikel selanjutnya.

Bonus: Mencetak jumlah biji dalam genggaman di dekat posisi lubang

Pada fungsi distribusi_biji() dan menembak_biji() terdapat perintah printf("[%2d]\n", genggaman); untuk mencetak berapa jumlah biji yang digenggam. Biji ini dicetak pada baris baru. Agar lebih jelas, kita bisa pindahkan cetakan ini di dekat posisi lubang dengan menggunakan perintah gotoxy().

Berikut ini kode untuk fungsi gotoxy(). Fungsi ini berguna untuk memindahkan posisi kursor untuk output.

/**
 * Pindah posisi kursor ke x,y
 * 
 * @param x Posisi kolom
 * @param y Posisi baris
 */
void gotoxy(int x, int y) {
  printf("\x1b[%d;%df", y, x);
}

Kemudian kita ganti perintah printf("[%2d]\n", genggaman); menjadi cetak_genggaman(index, genggaman);. Berikut ini isi fungsinya.

/**
 * Cetak jumlah biji dalam genggaman di dekat lubang
 * 
 * @param index Index lubang
 * @param jumlah Banyaknya biji dalam genggaman
 */
void cetak_genggaman(int index, int jumlah) {
  if ((index >= 8) && (index <= 14)) {
    gotoxy(5 + 5 * (14 - index), 2);
  }
  else if ((index >= 0) && (index <= 6)) {
    gotoxy(5 + 5 * index, 6);
  }
  else if (index == 7) {
    gotoxy(39, 5);
  }
  else {
    gotoxy(1, 3);
  }
  printf("[%2d]", jumlah);
  fflush(stdout);
}

Unduh kode program di sini.

1 komentar untuk “Membuat Permainan Congklak dengan Bahasa C (Bagian 1)”

  1. Ping-kembali: Membuat Permainan Congklak dengan Bahasa C (Bagian 2) – Meramu Koding

Tulis Komentar