Panduan lengkap TUBES 1: simulasi bus rental mobil (Soal 2.38)
Panduan ini menjelaskan seluruh tugas dari awal sampai akhir dalam satu berkas: tugas ini untuk
apa, soalnya apa, kenapa dijawab dengan simulasi, kenapa model dan kodenya dibuat seperti ini, dan
bagaimana mempersiapkan pengumpulan dan demo. Kode yang dijelaskan adalah src/bus-sim.c di repo
tubes1-simlib-bus (606 baris sebelum statistik, 683 baris sekarang, satuan waktu menit). Seluruh
isi bus-sim.c dan Makefile ada di sini, jadi tidak perlu membuka berkas lain.
Untuk setiap bagian, panduan ini menjawab empat pertanyaan:
- Untuk apa / kenapa ada: kalimat mana di soal yang dijawabnya.
- Kenapa dibutuhkan: apa yang rusak kalau bagian itu tidak ada.
- Cara kerjanya: langkah demi langkah.
- Kenapa kodenya begini: pilihan desain yang diambil dan alternatif yang tidak dipilih.
| Bagian | Isi |
|---|---|
| A | tugas ini untuk apa, dan kenapa dijawab dengan simulasi |
| B | soal 2.38 dalam bahasa biasa, dan keputusan tafsir yang kita ambil |
| C | dari kalimat soal ke bagian kode |
| D | glosarium kata teknis, termasuk penjelasan lengkap stream dan stream 6 |
| 0 | gambaran besar file bus-sim.c |
| 0A | istilah SIMLIB, simbol, dan rumus yang dipakai |
| 1 | urutan baca |
| 2 | seluruh isi bus-sim.c beserta penjelasan dan alasan desainnya |
| 3 | menelusuri hasil make trace |
| 4 | mesin status bus |
| 5 | perbandingan dengan versi satu berkas kita (bus_sim.c) |
| 6 | statistik (a) sampai (f): hasil nyata dan pemeriksaan kewajaran |
| 7 | apa yang boleh dan tidak boleh diklaim |
| 8 | soal latihan |
| 9 | cara menjalankan dari nol |
| J | pengumpulan dan persiapan demo |
| K | pertanyaan yang mungkin ditanyakan dosen, beserta jawabannya |
Bagian A: tugas ini untuk apa
TUBES I Pemodelan dan Simulasi. Kita diminta membuat simulasi untuk soal 2.38 di buku Law & Kelton, memakai C dan SIMLIB, lalu mengumpulkan tiga hal dalam satu PDF dan mendemokannya di akhir waktu UTS:
| Yang dikumpulkan | Isi |
|---|---|
| Deskripsi masalah | versi soal menurut program kita, satu halaman (kelompok boleh menafsirkan soal berbeda, asalkan ditulis) |
| Source code | harus sama dengan yang didemokan |
| Output | harus sama dengan yang didemokan |
Tujuan belajarnya: memodelkan sistem antrian yang tidak sederhana sebagai simulasi event-driven, mengumpulkan statistik darinya, dan memeriksa bahwa hasilnya masuk akal.
Kenapa dijawab dengan simulasi, bukan rumus
Sistem ini punya banyak hal sekaligus: tiga antrian di tempat berbeda, satu pelayan yang berpindah-pindah (bus), kapasitas 20, minimal 5 menit berhenti di tiap lokasi, serta waktu naik dan turun yang acak per orang. Rumus teori antrian seperti M/M/1 hanya berlaku untuk pelayan yang diam di satu tempat dan aturan yang lebih sederhana. Untuk sistem seperti ini tidak ada rumus tertutup. Jalan keluarnya: menjalankan sistem itu di komputer dan mencatat apa yang terjadi, lalu menghitung rata-rata dari catatan itu. Itulah simulasi.
Apa itu simulasi event-driven (satu paragraf)
Simulasi tidak berdetak per detik. Ia hanya memproses kejadian (event) satu per satu menurut waktunya: orang datang, bus tiba, selesai naik, selesai turun, dan seterusnya. Jam simulasi melompat dari satu kejadian ke kejadian berikutnya. Karena itu 80 jam waktu simulasi (4800 menit) selesai dalam sekejap di komputer. SIMLIB menyediakan mesin dasarnya (daftar kejadian, antrian, bilangan acak, pencatat statistik); kita menulis aturan kejadian-kejadiannya.
Apa yang dihasilkan
Enam kelompok angka di akhir 80 jam: (a) jumlah orang di antrian, (b) lama menunggu, (c) jumlah orang di bus, (d) lama bus berhenti, (e) lama satu putaran, (f) lama orang berada di sistem. Angka-angka ini adalah perkiraan dari satu kali jalan, bukan nilai pasti sistem (lihat Bagian 7).
Bagian B: soal 2.38 dalam bahasa biasa
Ceritanya
Sebuah rental mobil punya tiga lokasi: terminal udara 1, terminal udara 2, dan tempat rental mobil (lokasi 3). Orang datang ke tiap lokasi secara acak (14, 10, dan 24 orang per jam) lalu antre menunggu bus. Ada satu bus berkapasitas 20 orang yang berputar terus: 3 → 1 → 2 → 3. Orang dari terminal selalu ingin ke rental mobil. Orang dari rental mobil ingin ke terminal 1 (peluang 0,583) atau terminal 2 (peluang 0,417).
Rute (Gambar 2.72)
Angka pada gambar adalah panjang ruas jalan dalam mil. Bus berputar berlawanan arah jarum jam (panah bus di sisi bawah menunjuk ke kanan).
3
TL ─────────────── TR
│ │
0.5 1
│ │
(1) Air terminal 1 │
│ (3) Car rental ← bus mulai di sini
1 │
│ 1
(2) Air terminal 2 │
│ │
0.5 │
│ 3 Bus →│
BL ─────────────── BR
Karena bus hanya berhenti di (1), (2), (3), jarak antar-halte dijumlahkan dari ruas yang dilewati:
| Dari → ke | Ruas yang dilewati | Jarak | Waktu tempuh (30 mph) |
|---|---|---|---|
| 3 → 1 | naik 1, lewat atas 3, turun 0,5 | 4,5 mil | 9 menit |
| 1 → 2 | langsung 1 | 1 mil | 2 menit |
| 2 → 3 | turun 0,5, lewat bawah 3, naik 1 | 4,5 mil | 9 menit |
Satu putaran = 10 mil = 20 menit jalan, belum termasuk waktu berhenti.
Aturan di tiap halte
| Urutan | Aturan | Waktu |
|---|---|---|
| 1 | orang yang tujuannya halte ini turun dulu, urutan FIFO | Uniform(16, 24) detik per orang |
| 2 | lalu orang di antrian naik sampai kapasitas 20 | Uniform(15, 25) detik per orang |
| 3 | bus berhenti minimal 5 menit; setelah 5 menit, bila tidak ada yang sedang naik atau turun, bus langsung berangkat |
Yang diminta
Simulasi 80 jam dan enam statistik (a) sampai (f) di atas, dengan penugasan bilangan acak: stream 1 sampai 3 untuk antar-kedatangan di lokasi 1 sampai 3, stream 4 untuk waktu turun, stream 5 untuk waktu naik, stream 6 untuk tujuan penumpang yang tiba di rental mobil. (Stream = “dadu” bernomor untuk bilangan acak; penjelasan lengkap di Bagian D.)
Hal yang soal tidak jelaskan, dan keputusan kita
Soal sengaja tidak lengkap di beberapa hal. Setiap kelompok boleh menafsirkan, asal menuliskannya. Keputusan kita, dengan alasan dan alternatifnya:
| Hal yang tidak jelas | Keputusan kita | Alasan | Alternatif |
|---|---|---|---|
| Jarak antar-halte | 4,5 / 1 / 4,5 mil, dijumlahkan dari ruas di gambar | gambar hanya memberi panjang ruas; bus hanya berhenti di tiga lokasi | kelompok lain bisa membaca gambar berbeda |
| Bus pada t = 0 | sudah meninggalkan rental mobil, langsung menuju lokasi 1 | soal: bus “leaves immediately”; antrian masih kosong di t = 0 | memodelkan satu pemberhentian 5 menit di awal (tidak dipilih karena soal menyuruh langsung berangkat) |
| Kapan tujuan penumpang rental ditentukan | saat ia tiba di antrian | stream 6 dinamai “destination of an arrival”, dan dengan begitu hanya dipakai sekali per orang | saat naik bus (hasil acak bisa berbeda) |
| Orang yang datang saat bus masih berhenti di lokasinya | langsung naik bila bus idle dan masih ada kursi | bus masih di tempat dan ada kursi, jadi wajar ikut dimuat | ”snapshot”: hanya yang sudah antre saat bus tiba |
| Maksud “5 menit lalu berangkat” | berangkat tepat saat 5 menit lewat dan tidak ada proses naik/turun; bila proses masih berjalan di menit ke-5, ditunggu selesai | mengikuti kalimat soal apa adanya | memotong proses di menit ke-5 (tidak masuk akal secara fisik) |
| Kapasitas | menghitung semua orang di bus, termasuk yang hanya lewat | kursi tetap terpakai | hanya menghitung yang turun di halte berikutnya |
| Delay antrian | dihitung sampai orang diambil dari antrian | menunggu berakhir saat pelayanan dimulai | sampai selesai naik |
| Lama di sistem | dari tiba sampai selesai turun | itu saat orang benar-benar keluar dari sistem | sampai mulai turun |
| Jumlah di bus (c) | orang keluar dari bus di awal bongkar | jumlah langsung berkurang saat orang mulai turun | di akhir bongkar (angka rata-rata sedikit lebih tinggi) |
| Yang belum selesai di menit 4800 | tidak dihitung di (b), (e), (f) | tidak ada nilai akhir untuk mereka | memperpanjang simulasi sampai semua selesai |
Semua keputusan ini harus ditulis di deskripsi satu halaman; itulah gunanya halaman itu.
Bagian C: dari kalimat soal ke bagian kode
| Kalimat soal | Bagian kode | Output |
|---|---|---|
| orang datang di lokasi i dengan antar-kedatangan eksponensial, laju 14, 10, 24 per jam | location_set (laju menjadi menit per orang), event_arrival, schedule_next_arrival | n di (b) mendekati 14 × 80, 10 × 80, 24 × 80 |
| tiap lokasi punya antrian FIFO tanpa batas | list 1-3, passenger_file, passenger_remove_first | (a), (b) |
| satu bus, kapasitas 20, 30 mph | BUS_CAPACITY, BUS_SPEED_MPH, bus_is_full, location_set | (c), (e) |
| bus mulai di lokasi 3 dan langsung berangkat berlawanan arah jarum jam | bus_init, init_model, next_stop | trace: bus tiba di lokasi 1 pada menit 9 |
| orang dari terminal ke rental mobil; dari rental ke terminal 1 (0,583) atau 2 (0,417) | passenger_create dengan stream 6 | (f) per lokasi asal |
| turun dulu, FIFO, U(16,24) detik | try_start_unload, event_end_unload, stream 4 | (f) |
| lalu naik sampai kapasitas, U(15,25) detik | try_start_load, event_end_load, stream 5 | (b), (c) |
| minimal 5 menit; bila tidak ada proses setelah 5 menit, langsung berangkat | MIN_STOP_TIME, event_min_stop_reached, try_depart, bus_depart | (d) minimum 5,000 |
| simulasi 80 jam | SIM_LENGTH = 4800 menit, event akhir simulasi | judul laporan |
| statistik (a) sampai (f) | hook stats_* dan stats_report | tabel hasil |
Bagian D: glosarium kata teknis
Daftar istilah yang muncul di soal, di kode, dan di laporan. Dikelompokkan menurut tema. Kalau ada kata yang masih membingungkan dan belum ada di sini, tambahkan di sini.
D1. Stream dan bilangan acak (penjelasan lengkap)
Stream = satu “dadu” bernomor. SIMLIB punya satu pembangkit bilangan acak (lcgrand) yang dapat
menjalankan banyak urutan bilangan acak yang saling terpisah. Tiap urutan diberi nomor, dan nomor itulah
yang disebut stream (stream 1, stream 2, dan seterusnya sampai 100). Setiap kali kita meminta angka dari
sebuah stream, kita mendapat bilangan acak antara 0 dan 1 (misalnya 0,42 atau 0,90), dan angka berikutnya dari
stream yang sama selalu berbeda.
Tiga sifat penting:
| Sifat | Artinya |
|---|---|
| Tiap stream punya titik awal sendiri | disebut seed; sudah diset di simlib.c untuk semua stream |
| Urutannya tetap | karena angka berikutnya ditentukan oleh angka sebelumnya, menjalankan program dua kali memberi hasil yang sama persis (hasil bisa diulang) |
| Antar-stream terpisah | angka yang diambil dari stream 4 tidak memengaruhi angka di stream 5 |
Kenapa dipisah? Misalkan semua kebutuhan acak memakai satu urutan. Kalau kita mengubah satu hal (misalnya cara memilih waktu naik), jumlah angka yang terambil berubah, dan semua angka acak sesudahnya bergeser: antar-kedatangan, tujuan penumpang, semuanya ikut berubah. Dengan stream terpisah, setiap sumber ketidakpastian punya dadu sendiri, sehingga perubahan di satu tempat tidak mengubah hasil acak di tempat lain. Itu juga membuat perbandingan antar versi program adil. Karena itu soal menetapkan nomor stream untuk tiap kebutuhan acak:
| Stream | Dipakai untuk | Dalam kode |
|---|---|---|
| 1, 2, 3 | waktu antar-kedatangan di lokasi 1, 2, 3 | STREAM_INTERARRIVAL(loc) |
| 4 | waktu menurunkan satu orang | STREAM_UNLOAD |
| 5 | waktu menaikkan satu orang | STREAM_LOAD |
| 6 | tujuan orang yang baru tiba di rental mobil | STREAM_DESTINATION |
Stream 6 secara khusus. Orang yang tiba di rental mobil harus memilih tujuan: terminal 1 (peluang 0,583) atau
terminal 2 (peluang 0,417). Pemilihan itu “dilempar” dengan dadu khusus, yaitu stream 6. Caranya: setiap orang
yang tiba di rental mengambil satu angka u dari stream 6.
Angka u dari stream 6 | Tujuan |
|---|---|
u kurang dari 0,583 (misalnya 0,42) | terminal 1 |
u 0,583 atau lebih (misalnya 0,90) | terminal 2 |
Di kode ini ada di passenger_create: lcgrand(STREAM_DESTINATION) < PROB_RENTAL_TO_T1. Orang dari terminal tidak memakai
stream 6, karena tujuannya pasti rental mobil. Jadi jumlah angka yang diambil dari stream 6 sama dengan jumlah orang
yang tiba di rental (sekitar 24 × 80 = 1920 dalam satu kali jalan). Stream 6 hanya menentukan tujuan; waktu
datang orang rental memakai stream 3.
D2. Simulasi dan waktu
| Istilah | Artinya |
|---|---|
| Simulasi | menjalankan model sistem di komputer untuk melihat perilakunya, dan mencatat statistiknya |
| Event (kejadian) | sesuatu yang mengubah keadaan sistem pada satu saat: orang datang, bus tiba, selesai naik, selesai turun, batas 5 menit tercapai, simulasi berakhir |
| Event-driven | jam simulasi hanya melompat ke kejadian berikutnya, bukan berdetak per detik |
Jam simulasi (sim_time) | waktu di dalam simulasi (menit); bukan waktu komputer |
| Daftar event (event list) | daftar kejadian yang sudah dijadwalkan, terurut menurut waktunya |
Jadwalkan (event_schedule) | menaruh satu kejadian baru ke daftar event untuk terjadi di waktu tertentu |
| Replikasi | satu kali menjalankan simulasi penuh; kita hanya punya satu replikasi (seed bawaan) |
| Seed | titik awal urutan bilangan acak (lihat D1) |
| Trace | mencetak jalannya event satu per satu untuk memeriksa kronologi |
D3. Antrian dan sistem bus
| Istilah | Artinya |
|---|---|
| Antrian (queue) | orang yang menunggu bus di satu lokasi |
| FIFO | first in, first out: yang datang lebih dulu dilayani lebih dulu |
| Kapasitas | jumlah maksimum orang di bus (20); semua orang di bus dihitung |
| Halte / stop / lokasi | tempat bus berhenti: terminal 1, terminal 2, rental mobil |
| Putaran (loop) | dari bus berangkat dari rental mobil sampai berangkat lagi dari sana |
| Lama berhenti (dwell, stop time) | dari bus tiba di suatu halte sampai bus berangkat dari halte itu |
| Delay | lama seseorang menunggu di antrian, sampai diambil untuk naik |
| Lama di sistem | dari seseorang tiba sampai selesai turun di tujuannya |
| Status bus | MOVING (di jalan), IDLE (berhenti, tidak ada proses), UNLOADING (ada yang sedang turun), LOADING (ada yang sedang naik) |
| Idle | berhenti dan tidak sedang naik atau turun |
| Mph | mil per jam, satuan kecepatan bus (30) |
D4. Distribusi (cara angka acak dibentuk)
| Istilah | Artinya |
|---|---|
| Eksponensial | bentuk sebaran waktu antar-kedatangan; banyak jeda pendek, sedikit jeda panjang; dibentuk dengan expon(rata-rata, stream) |
| Poisson | kedatangan acak independen dengan laju tetap; waktu antar-kedatangannya eksponensial |
| Waktu antar-kedatangan (interarrival) | jeda antara dua orang yang datang berurutan di satu lokasi |
| Uniform(a, b) | semua nilai antara a dan b sama mungkin; dipakai untuk waktu naik (15 sampai 25 detik) dan turun (16 sampai 24 detik) |
| Laju | banyak kejadian per satuan waktu (14 orang per jam); kebalikannya adalah rata-rata antar-kedatangan (60 ÷ 14 menit) |
D5. Kata-kata SIMLIB
| Istilah | Artinya |
|---|---|
| SIMLIB | pustaka C dari buku Law & Kelton: daftar event, list, bilangan acak, pencatat statistik |
| List | antrian bernomor di SIMLIB; list 1-3 = antrian di lokasi, list 4-6 = penumpang di bus menurut tujuan |
list_file / list_remove | memasukkan ke ujung list / mengambil dari depan list |
list_size[n] | jumlah isi list nomor n sekarang |
transfer[] | ”amplop” data sementara untuk list; kolom 1 dan 2 ditimpa SIMLIB tiap event_schedule dan timing |
| Atribut | satu kolom data di transfer[] |
maxatr | jumlah kolom transfer[] yang dipakai (minimal 4) |
timing() | mengambil kejadian terdekat dan memajukan jam simulasi |
sampst | mencatat rata-rata per kejadian (delay, lama berhenti, dan seterusnya) |
timest | mencatat rata-rata terhadap waktu untuk level yang berubah (jumlah di bus) |
filest | laporan otomatis panjang list dari waktu ke waktu |
| Hook | fungsi kecil stats_* yang dipanggil model pada saat tertentu untuk menitipkan angka ke pencatat |
| Sentinel (1e30) | angka besar yang dipakai SIMLIB bila belum ada data; tidak boleh dicetak apa adanya |
D6. Statistik dan pemeriksaan
| Istilah | Artinya |
|---|---|
| Rata-rata per kejadian | jumlah semua nilai dibagi banyaknya kejadian (delay: sampst) |
| Rata-rata terhadap waktu | rata-rata level yang dibobot lamanya tiap level bertahan (jumlah di bus: timest) |
| Maks / Min | nilai terbesar / terkecil yang pernah tercatat |
| n | banyaknya observasi |
| Hukum Little | rata-rata jumlah dalam sistem = laju datang × rata-rata lama di sistem; dipakai untuk memeriksa kewajaran |
| Verifikasi | memeriksa bahwa program sesuai model dan hasilnya wajar |
| Validasi | memeriksa bahwa model cocok dengan sistem nyata |
| Kewajaran (sanity check) | pemeriksaan sederhana, misalnya maks di bus tidak mungkin lebih dari 20 |
D7. Kata-kata di kode C
| Istilah | Artinya |
|---|---|
#define | memberi nama pada sebuah angka atau potongan teks (misalnya BUS_CAPACITY untuk 20) |
| Makro berparameter | #define yang menerima nilai, misalnya LIST_QUEUE(loc); bukan fungsi, hanya penggantian teks |
struct | satu paket beberapa data (misalnya Passenger: waktu tiba, asal, tujuan) |
enum | daftar nilai bernama (misalnya status bus BUS_MOVING, BUS_IDLE, …) |
| Prototipe | daftar nama fungsi di bagian atas file supaya urutan penulisan fungsi bebas |
| Flag | variabel 0 atau 1 sebagai penanda (misalnya min_stop_reached) |
| Global | variabel yang dapat dipakai semua fungsi (misalnya bus, locations) |
Bagian 0: gambaran besar
Soal 2.38: tiga lokasi (terminal 1, terminal 2, rental mobil) dilayani satu bus yang berputar 3 → 1 → 2 → 3, dengan jarak antar-halte 4,5 / 1 / 4,5 mil (tafsiran atas Gambar 2.72).
Seluruh model ada di satu file yang dibagi menjadi bagian-bagian berurutan, seperti jobshop.c
di buku:
bus-sim.c
├─ parameter soal, penomoran SIMLIB, tipe data, variabel global (baris 1-157)
├─ main(): loop event (baris 158-224)
├─ init_model(), report() (baris 226-255)
├─ entitas: location, passenger, bus (baris 257-378)
├─ event: arrival, loading, unloading, bus arrival / 5 menit / berangkat (baris 380-525)
├─ statistik (a)-(f): hook dan laporan (baris 527-643)
└─ trace (opsional) (baris 645-683)
Aturan desain dari rancangan temanmu: bagian statistik tidak mengubah keadaan model, hanya menerima
angka dari hook. Karena itu mengisi statistik hanya menyentuh fungsi stats_*, bukan model.
Siapa pemilik apa:
| Bagian | Pemilik | Status |
|---|---|---|
model di src/bus-sim.c (selain stats_*), Makefile, docs/DESIGN.md | temanmu | selesai, bisa di-compile dan jalan |
fungsi stats_* di src/bus-sim.c | kamu (diisi bersama asisten) | sudah diisi, baru di salinan lokal, belum di-commit |
lib/simlib/ | buku Law & Kelton | jangan diklaim ditulis sendiri |
Bagian 0A: istilah SIMLIB, simbol, dan rumus
Semua yang dipakai di kode ada di sini. Tidak perlu membuka catatan lain.
Cara kerja SIMLIB dalam lima kalimat
- Simulasi berjalan dari kejadian ke kejadian (event-scheduling). Waktu simulasi
sim_timemelompat ke waktu kejadian berikutnya, bukan berdetak per detik. Karena itu 80 jam simulasi selesai dalam sekejap. - Ada daftar kejadian yang terurut menurut waktu.
event_schedule(waktu, tipe)menambah kejadian ke daftar itu;timing()mengambil yang paling awal dan memajukansim_time. - Ada list bernomor (antrian).
list_file(LAST, n)memasukkan satu data ke ujung listn;list_remove(FIRST, n)mengambil dari depan listn(jadi FIFO).list_size[n]adalah isinya sekarang. - Data yang dimasukkan atau diambil dari list dibawa lewat larik
transfer[]. Kolom 1 dan 2 dipakai SIMLIB sendiri untuk waktu dan tipe event, dan ditimpa setiapevent_schedule()atautiming(). Karena itu data penting harus segera disalin keluar daritransfer[]. - SIMLIB punya pencatat statistik:
sampst(rata-rata per kejadian),timestdanfilest(rata-rata terhadap waktu).
Tabel istilah
| Yang kamu lihat | Artinya |
|---|---|
sim_time | jam simulasi sekarang (variabel global SIMLIB) |
event_schedule(waktu, tipe) | catat: nanti jalankan kejadian tipe pada waktu |
timing() | ambil kejadian terdekat, majukan sim_time, isi next_event_type |
transfer[n] | amplop data sementara; kolom 1 dan 2 direbut otomatis oleh SIMLIB |
list_file(FIRST/LAST, n) | masukkan isi transfer[] ke depan/ujung list n |
list_remove(FIRST/LAST, n) | ambil dari depan/ujung list n, isinya kembali ke transfer[] |
list_size[n] | jumlah elemen di list n sekarang |
maxatr | jumlah kolom transfer[] yang dipakai (SIMLIB mewajibkan minimal 4) |
expon(mean, stream) | angka acak eksponensial dengan rata-rata mean dari stream tertentu |
uniform(a, b, stream) | angka acak seragam antara a dan b |
lcgrand(stream) | angka acak mentah antara 0 dan 1 dari stream tertentu |
| stream | ”dadu” bilangan acak bernomor (penjelasan lengkap di Bagian D). Soal menetapkan: 1-3 antar-kedatangan lokasi 1-3, 4 bongkar, 5 muat, 6 tujuan penumpang dari rental |
sampst(nilai, var) | var positif: catat satu observasi. var negatif: minta laporan (hasil di transfer[1..4] = rata-rata, jumlah, maks, min) |
timest(nilai, var) | seperti sampst tetapi untuk level yang berubah sepanjang waktu. var negatif: laporan di transfer[1..3] = rata-rata terhadap waktu, maks, min |
filest(list) | laporan otomatis panjang list dari waktu ke waktu (transfer[1..3] seperti timest) |
Simbol dan rumus yang muncul
| Simbol | Dibaca | Artinya |
|---|---|---|
| D ke-i | delay (lama menunggu) orang ke-i | |
| jumlah observasi | ||
| sigma | jumlahkan semuanya | |
| , , | d, q, u “topi” | hasil perkiraan dari simulasi, bukan nilai pasti |
| lambda | laju kedatangan (orang per jam) | |
| mu | laju layanan | |
| rho | , intensitas lalu lintas |
Tiga rumus, dengan angka kecil:
- Rata-rata delay: . Rata-rata biasa. Tiga orang menunggu 0, 2, dan 4 jam: jam.
- Rata-rata panjang antrian: , dengan lama waktu antrian berisi tepat orang dan lama total. Dibobot lamanya waktu. Dalam 6 jam antrian kosong 3 jam, berisi 1 orang 2 jam, berisi 2 orang 1 jam: .
- Utilisasi: = persentase waktu server sibuk. Sibuk 4 dari 6 jam: .
Rumus angka acak eksponensial (sudah ada di expon): , dengan angka acak 0 sampai 1 dan rata-rata yang diinginkan. Contoh: , memberi .
Hukum Little untuk mengecek kewajaran: rata-rata jumlah dalam sistem = laju datang × rata-rata lama di sistem, yaitu . Dipakai di Bagian 6.
Dua jenis rata-rata (penting untuk soal ini):
| Jenis | Contoh | Fungsi SIMLIB |
|---|---|---|
| per kejadian | delay, lama di sistem, lama bus berhenti, lama putaran | sampst |
| terhadap waktu | panjang antrian, jumlah penumpang di bus | filest, timest |
Validasi vs verifikasi: validasi = apakah model cocok dengan sistem nyata; verifikasi = apakah program sesuai model dan hasilnya wajar.
Bagian 1: urutan baca
Urutannya dari data → benda → kejadian → perakitan → pengukuran, bukan dari baris pertama. Kalau
dibaca dari main duluan, kamu bertemu nama-nama yang belum kamu kenal.
| Langkah | Bagian di bus-sim.c | Baris | Inti yang harus kamu tangkap |
|---|---|---|---|
| 1 | parameter soal | 1-35 | semua angka dari soal, satuan menit |
| 2 | penomoran SIMLIB | 36-80 | event, list, atribut, stream, nomor statistik |
| 3 | tipe data, variabel global, prototipe | 81-157 | bentuk Location, Passenger, Bus |
| 4 | passenger | 280-325 | satu orang, dan cara dia disimpan di list SIMLIB |
| 5 | location | 257-279 | tiga lokasi, rute, konversi ke menit |
| 6 | bus | 326-379 | status bus dan penumpang di dalamnya |
| 7 | arrival | 380-407 | orang datang |
| 8 | unloading | 439-471 | orang turun |
| 9 | loading | 408-438 | orang naik |
| 10 | bus tiba, 5 menit, berangkat | 472-526 | siklus bus |
| 11 | main, init_model, report | 158-256 | loop yang menggerakkan semuanya |
| 12 | statistik | 527-644 | pencatat statistik (a) sampai (f) |
| 13 | trace | 645-683 | alat melihat jalannya simulasi |
| 14 | Makefile | cara compile dan menjalankan |
Setelah itu ada penelusuran hasil nyata (Bagian 3), mesin status bus (Bagian 4), perbandingan
dengan bus_sim.c (Bagian 5), pengerjaan statistik (Bagian 6), batas klaim (Bagian 7), latihan
(Bagian 8), dan cara menjalankan (Bagian 9).
Bagian 2: kode dan penjelasannya
Setiap blok kode diberi nomor baris di src/bus-sim.c. Seluruh 683 baris ada di sini, hanya
disusun menurut urutan baca.
Langkah 1: parameter soal (baris 1 sampai 35)
src/bus-sim.c baris 1-35
/* Car-rental bus simulation, Law & Kelton Problem 2.38. All times in minutes. */
#include "simlib.h"
/* Problem parameters. */
#define MINUTES_PER_HOUR 60.0
#ifndef SIM_LENGTH
#define SIM_LENGTH (80.0 * MINUTES_PER_HOUR) /* 80 hours */
#endif
#define BUS_CAPACITY 20
#define BUS_SPEED_MPH 30.0
#define MIN_STOP_TIME 5.0 /* 5 minutes */
#define UNLOAD_TIME_MIN (16.0 / 60.0) /* 16 seconds */
#define UNLOAD_TIME_MAX (24.0 / 60.0) /* 24 seconds */
#define LOAD_TIME_MIN (15.0 / 60.0) /* 15 seconds */
#define LOAD_TIME_MAX (25.0 / 60.0) /* 25 seconds */
#define ARRIVAL_RATE_T1 14.0 /* people per hour */
#define ARRIVAL_RATE_T2 10.0
#define ARRIVAL_RATE_RENTAL 24.0
#define PROB_RENTAL_TO_T1 0.583 /* else terminal 2 */
#define DIST_RENTAL_TO_T1 4.5 /* miles */
#define DIST_T1_TO_T2 1.0
#define DIST_T2_TO_RENTAL 4.5
#ifndef ENABLE_TRACE
#define ENABLE_TRACE 0 /* 1 = print each event */
#endif
#define OUTPUT_FILE "bus-sim.out"
- Kenapa ada: soal memberi banyak angka (kapasitas 20, 30 mph, 5 menit, U(16,24) detik, laju 14/10/24 per jam, peluang 0,583, jarak, 80 jam). Semuanya dikumpulkan di bagian atas file.
- Kenapa dibutuhkan: kalau angka tersebar di banyak tempat, satu salah ubah membuat hasil keliru tanpa ada yang sadar. Di satu tempat, angka bisa dicocokkan langsung dengan soal.
- Cara kerja: hanya
#define. Satuan waktu menit:SIM_LENGTH= 80 jam × 60 = 4800 menit;MIN_STOP_TIME= 5,0; waktu bongkar dan muat ditulis16.0 / 60.0menit (16 detik ÷ 60 detik per menit). Laju kedatangan tetap per jam dan kecepatan tetap mph; keduanya dikonversi sekali dilocation_set(Langkah 5).SIM_LENGTHdanENABLE_TRACEdibungkus#ifndefsupaya bisa diganti dari baris compile (-DSIM_LENGTH=60), dipakaimake trace. - Kenapa kodenya begini: angka soal dikumpulkan di bagian atas supaya satu perubahan cukup di satu tempat dan mudah dicocokkan dengan soal. Satuannya menit karena data soal bercampur (jam, detik, mil per jam); kalau ditulis dalam jam, 5 menit menjadi 0,0833 dan hasilnya sulit dibaca. Memakai jam juga benar, asalkan semua konstanta ikut satu satuan. Konversi sengaja dilakukan sekali (di
#definedan dilocation_set) supaya tidak ada angka bersatuan campur di tengah kode. - Uji diri: kenapa ditulis
16.0 / 60.0, bukan16 / 60? (Di C, pembagian dua bilangan bulat dibulatkan ke bawah, jadi16 / 60bernilai 0 dan waktu bongkar muat jadi nol.)
Langkah 2: penomoran SIMLIB (baris 36 sampai 80)
src/bus-sim.c baris 36-80
/* Locations. */
#define LOC_TERMINAL_1 1
#define LOC_TERMINAL_2 2
#define LOC_CAR_RENTAL 3
#define NUM_LOCATIONS 3
/* Event types. Arrival event number = location number. */
#define EVENT_ARRIVAL_T1 1
#define EVENT_ARRIVAL_T2 2
#define EVENT_ARRIVAL_RENTAL 3
#define EVENT_BUS_ARRIVAL 4
#define EVENT_END_UNLOAD 5
#define EVENT_END_LOAD 6
#define EVENT_MIN_STOP_REACHED 7
#define EVENT_END_SIMULATION 8
/* Lists: queue at each location (1-3), passengers on the bus by destination (4-6). */
#define LIST_QUEUE(loc) (loc)
#define LIST_BUS(dest) (NUM_LOCATIONS + (dest))
/* Passenger attributes in transfer[]. */
#define ATTR_ARRIVAL_TIME 1
#define ATTR_ORIGIN 2
#define ATTR_DESTINATION 3
#define NUM_ATTRIBUTES 4
/* Random-number streams. */
#define STREAM_INTERARRIVAL(loc) (loc)
#define STREAM_UNLOAD 4
#define STREAM_LOAD 5
#define STREAM_DESTINATION 6
/* Statistic variables for sampst/timest (used by the stats functions). */
#define SAMPST_QUEUE_DELAY(loc) (loc)
#define SAMPST_STOP_TIME(loc) (NUM_LOCATIONS + (loc))
#define SAMPST_LOOP_TIME 7
#define SAMPST_TIME_IN_SYSTEM(loc) (7 + (loc))
#define TIMEST_NUM_ON_BUS 1
- Kenapa ada: SIMLIB hanya mengenal nomor (nomor event, list, stream, variabel statistik). Bagian ini memberi nama pada nomor-nomor itu.
- Kenapa dibutuhkan: tanpa nama, kode penuh angka mentah seperti
list_file(LAST, 4)yang tidak terbaca dan mudah tertukar. - Cara kerja:
- Event: kedatangan di lokasi 1/2/3 memakai tipe 1/2/3 (nomor event = nomor lokasi), lalu 4 bus tiba, 5 selesai turun, 6 selesai naik, 7 batas 5 menit tercapai, 8 akhir simulasi.
- List:
LIST_QUEUE(loc)= list 1-3 (antrian),LIST_BUS(dest)= list 4-6 (penumpang di bus, dikelompokkan menurut tujuan). Sama denganbus_sim.ckita. - Atribut penumpang di
transfer[]: 1 = waktu tiba, 2 = lokasi asal, 3 = tujuan.NUM_ATTRIBUTES = 4karena SIMLIB mewajibkanmaxatrminimal 4. - Stream 1-3 antar-kedatangan, 4 bongkar, 5 muat, 6 tujuan penumpang rental, sesuai penugasan soal.
- Nomor variabel statistik sudah dipesan:
SAMPST_QUEUE_DELAY(loc)= 1-3,SAMPST_STOP_TIME(loc)= 4-6,SAMPST_LOOP_TIME= 7,SAMPST_TIME_IN_SYSTEM(loc)= 8-10,TIMEST_NUM_ON_BUS= 1.
- Kenapa kodenya begini: nomor event kedatangan sama dengan nomor lokasi, sehingga
event_schedule(waktu, location)danswitchbisa memakai nomor lokasi langsung. List bus dibagi menurut tujuan supaya orang yang harus turun langsung ketahuan darilist_size; alternatifnya satu list bus lalu memeriksa tujuan tiap orang, yang lebih rumit karenalist_removehanya bisa mengambil dari depan atau ujung. Nomor stream mengikuti soal supaya hasil bisa dibandingkan dengan program lain yang memakai stream sama. - Uji diri: kenapa list bus dikelompokkan menurut tujuan? (Supaya saat bus tiba di suatu lokasi, orang yang harus turun langsung ketahuan: tinggal ambil dari list tujuan itu.)
Langkah 3: tipe data, variabel global, prototipe (baris 81 sampai 157)
src/bus-sim.c baris 81-112
/* Entity types. */
typedef struct {
const char *name;
double mean_interarrival; /* minutes */
int next_stop;
double travel_time_to_next; /* minutes */
} Location;
typedef struct {
double arrival_time;
int origin;
int destination;
} Passenger;
typedef enum { BUS_MOVING, BUS_IDLE, BUS_UNLOADING, BUS_LOADING } BusStatus;
typedef struct {
int location; /* while moving: the next stop */
BusStatus status;
int min_stop_reached; /* 1 once 5 minutes have passed */
double arrival_time; /* arrival at the current stop */
double last_rental_departure;
Passenger current; /* passenger being loaded or unloaded */
} Bus;
/* Declare non-simlib global variables. */
Location locations[NUM_LOCATIONS + 1];
Bus bus;
FILE *outfile;
src/bus-sim.c baris 113-157
/* Declare non-simlib functions. */
void init_model(void);
void report(FILE *out);
void location_init(void);
void location_set(int id, const char *name, double arrival_rate,
int next_stop, double distance_to_next);
Passenger passenger_create(int origin);
void passenger_file(const Passenger *p, int list);
Passenger passenger_remove_first(int list);
void bus_init(void);
int bus_passenger_count(void);
int bus_is_full(void);
int bus_has_passenger_for(int destination);
void bus_board(const Passenger *p);
Passenger bus_alight(int destination);
void schedule_next_arrival(int location);
void event_arrival(int location);
int try_start_load(void);
void event_end_load(void);
int try_start_unload(void);
void event_end_unload(void);
void event_bus_arrival(void);
void event_min_stop_reached(void);
void try_depart(void);
void bus_depart(void);
void stats_init(void);
void stats_queue_delay(int location, double delay);
void stats_bus_count_changed(int passengers_on_bus);
void stats_bus_stop(int location, double stop_time);
void stats_bus_loop(double loop_time);
void stats_time_in_system(int origin, double time);
void stats_report_line(FILE *out, const char *label, double avg,
double max, double min, int max_is_count);
void stats_report_sampst(FILE *out, const char *label, int variable,
int show_min);
void stats_report(FILE *out);
const char *event_name(int type);
const char *bus_status_name(BusStatus status);
void trace_event(void);
- Kenapa ada: model punya tiga benda: lokasi, penumpang, bus. Masing-masing diberi
structsupaya datanya satu paket. - Kenapa dibutuhkan: statistik delay dan lama di sistem butuh tahu kapan tiap orang tiba dan dari mana; aturan bus butuh status dan posisi bus. Struct membuat itu terbaca jelas.
- Cara kerja:
Location: nama, rata-rata antar-kedatangan (menit), lokasi berikutnya, waktu tempuh ke lokasi berikutnya (menit).Passenger: waktu tiba, asal, tujuan.Bus:location(stop sekarang; saat bergerak, stop tujuan),status(BUS_MOVING,BUS_IDLE,BUS_UNLOADING,BUS_LOADING),min_stop_reached(1 setelah 5 menit lewat),arrival_timedi stop sekarang,last_rental_departure(untuk statistik putaran), dancurrent(penumpang yang sedang “di pintu” bus).- Variabel global
locations,bus,outfile. Blok prototipe hanya daftar fungsi supaya urutan penulisan fungsi bebas, miripjobshop.c.
- Kenapa kodenya begini: struct membuat data satu benda menjadi satu paket dan nama field menjelaskan dirinya. Status bus dibuat
enumberisi empat nilai, bukan beberapa flag boolean, karena bus selalu berada tepat dalam satu keadaan; flag terpisah bisa saling bertentangan (misalnya “memuat” dan “bergerak” sekaligus).Bus.currentada karena orang yang sedang naik atau turun sudah keluar dari list tetapi belum masuk list tujuan, jadi harus dititipkan di suatu tempat selama waktu layanannya. - Uji diri: kenapa ada status
BUS_IDLEterpisah dariBUS_MOVING? (IDLE = bus berhenti di stop tapi tidak ada proses; di keadaan ini penumpang yang baru datang boleh langsung naik dan bus boleh berangkat begitu 5 menit lewat.)
Langkah 4: passenger (baris 280 sampai 325)
src/bus-sim.c baris 280-325
/* Entity: passenger. Only these functions use transfer[] for passengers. */
Passenger passenger_create(int origin)
{
Passenger p;
p.arrival_time = sim_time;
p.origin = origin;
/* Terminal arrivals go to the car rental; car-rental arrivals go to
terminal 1 (prob. 0.583) or terminal 2, decided with stream 6. */
if (origin == LOC_CAR_RENTAL) {
if (lcgrand(STREAM_DESTINATION) < PROB_RENTAL_TO_T1)
p.destination = LOC_TERMINAL_1;
else
p.destination = LOC_TERMINAL_2;
} else {
p.destination = LOC_CAR_RENTAL;
}
return p;
}
void passenger_file(const Passenger *p, int list)
{
transfer[ATTR_ARRIVAL_TIME] = p->arrival_time;
transfer[ATTR_ORIGIN] = (double) p->origin;
transfer[ATTR_DESTINATION] = (double) p->destination;
list_file(LAST, list);
}
Passenger passenger_remove_first(int list)
{
Passenger p;
/* Copy right away: event_schedule() and timing() overwrite transfer[]. */
list_remove(FIRST, list);
p.arrival_time = transfer[ATTR_ARRIVAL_TIME];
p.origin = (int) transfer[ATTR_ORIGIN];
p.destination = (int) transfer[ATTR_DESTINATION];
return p;
}
- Kenapa ada: soal punya orang yang datang, antre, naik, turun, dan tiap orang punya tujuan sendiri.
- Kenapa dibutuhkan: statistik delay dan lama di sistem butuh waktu tiba dan asal tiap orang, yang harus ikut terbawa dari antrian ke bus sampai keluar.
- Cara kerja:
passenger_create(origin):arrival_time = sim_time. Dari terminal tujuannya pasti rental mobil. Dari rental mobil, diundi sekali dengan stream 6: angka acak < 0,583 berarti terminal 1, selain itu terminal 2.passenger_filemenyalin tiga field ketransfer[1..3], lalulist_file(LAST, list)menaruh orang di ujung list.passenger_remove_firstmengambil dari depan list (list_remove(FIRST, ...)), lalu langsung menyalinnya ke struct.
- Kenapa disalin segera (poin penting):
event_schedule()dantiming()menimpatransfer[1]dantransfer[2]. Data yang dibiarkan ditransfer[]bisa rusak. Ini bug yang sama dengan yang kita alami dibus_sim.c(di sana dihindari dengan memindah data ketransfer[3..5]). - Kenapa kodenya begini: data disalin ke struct agar tidak tertimpa SIMLIB. Menaruh data di
transfer[3..5]seperti dibus_sim.cjuga bekerja, tetapi setiap pemakai harus mengingat aturannya; dengan fungsi khusus, aturan itu hanya ada di tiga fungsi ini. Tujuan diundi saat orang datang agar stream 6 dipakai tepat sekali per orang rental dan urutan bilangan acak tidak bergantung pada kapan orang naik.lcgrand(stream) < 0,583dipakai, bukanrandom_integer, karena hanya ada dua pilihan. - Uji diri: kenapa tujuan orang rental diundi saat dia datang, bukan saat naik bus? (Stream 6 di soal menyebut “destination of an arrival at the car rental”, dan stream hanya dipakai sekali per orang.)
Langkah 5: location (baris 257 sampai 279)
src/bus-sim.c baris 257-279
/* Entity: location. The route 3 -> 1 -> 2 -> 3 is defined here only. */
void location_init(void)
{
location_set(LOC_TERMINAL_1, "Air terminal 1", ARRIVAL_RATE_T1,
LOC_TERMINAL_2, DIST_T1_TO_T2);
location_set(LOC_TERMINAL_2, "Air terminal 2", ARRIVAL_RATE_T2,
LOC_CAR_RENTAL, DIST_T2_TO_RENTAL);
location_set(LOC_CAR_RENTAL, "Car rental", ARRIVAL_RATE_RENTAL,
LOC_TERMINAL_1, DIST_RENTAL_TO_T1);
}
void location_set(int id, const char *name, double arrival_rate,
int next_stop, double distance_to_next)
{
/* Rate is per hour and speed is miles per hour; convert both to minutes. */
locations[id].name = name;
locations[id].mean_interarrival = MINUTES_PER_HOUR / arrival_rate;
locations[id].next_stop = next_stop;
locations[id].travel_time_to_next = distance_to_next / BUS_SPEED_MPH * MINUTES_PER_HOUR;
}
- Kenapa ada: soal punya tiga lokasi dengan laju berbeda dan rute melingkar.
- Kenapa dibutuhkan: rute 3 → 1 → 2 → 3 dan jaraknya dipakai di banyak tempat (kapan bus tiba, siapa lokasi berikutnya). Kalau rute ditulis di beberapa tempat, bisa tidak konsisten.
- Cara kerja:
location_initmengisi tabel tiga lokasi lewatlocation_set. Di sini terjadi konversi satuan:mean_interarrival = 60 / laju(laju per jam menjadi menit per orang) dantravel_time_to_next = jarak / kecepatan × 60(jam menjadi menit). Rute hanya didefinisikan di sini.
| id | nama | rata-rata antar-kedatangan | next_stop | waktu tempuh ke berikutnya |
|---|---|---|---|---|
| 1 | Air terminal 1 | 60/14 = 4,29 menit | 2 | 1,0/30 × 60 = 2 menit |
| 2 | Air terminal 2 | 60/10 = 6 menit | 3 | 4,5/30 × 60 = 9 menit |
| 3 | Car rental | 60/24 = 2,5 menit | 1 | 4,5/30 × 60 = 9 menit |
- Kenapa kodenya begini: tabel lokasi membuat rute dan laju ada di satu tempat, sehingga mengubahnya cukup di
location_init. Konversi satuan dilakukan dilocation_setkarena di situlah laju per jam dan jarak per mph bertemu satuan program. Indeks 0 tidak dipakai supaya nomor lokasi sama dengan indeks larik (1, 2, 3), sesuai kebiasaan SIMLIB yang juga memakai nomor list dan variabel mulai dari 1. - Uji diri: kalau rute diubah jadi 3 → 2 → 1 → 3, bagian mana yang diubah? (Hanya
location_init.)
Langkah 6: bus (baris 326 sampai 379)
src/bus-sim.c baris 326-379
/* Entity: bus. Passengers on board are in lists 4-6, one per destination. */
void bus_init(void)
{
/* At t = 0 the bus has just left the car rental, heading to terminal 1. */
bus.location = locations[LOC_CAR_RENTAL].next_stop;
bus.status = BUS_MOVING;
bus.min_stop_reached = 0;
bus.arrival_time = 0.0;
bus.last_rental_departure = 0.0;
bus.current.arrival_time = 0.0;
bus.current.origin = 0;
bus.current.destination = 0;
}
int bus_passenger_count(void)
{
int dest;
int count;
count = 0;
for (dest = 1; dest <= NUM_LOCATIONS; ++dest)
count += list_size[LIST_BUS(dest)];
return count;
}
int bus_is_full(void)
{
return bus_passenger_count() >= BUS_CAPACITY;
}
int bus_has_passenger_for(int destination)
{
return list_size[LIST_BUS(destination)] > 0;
}
/* Called at the end of loading. */
void bus_board(const Passenger *p)
{
passenger_file(p, LIST_BUS(p->destination));
stats_bus_count_changed(bus_passenger_count());
}
/* Called at the start of unloading; FIFO among passengers for this stop. */
Passenger bus_alight(int destination)
{
Passenger p;
p = passenger_remove_first(LIST_BUS(destination));
stats_bus_count_changed(bus_passenger_count());
return p;
}
- Kenapa ada: soal punya satu bus dengan kapasitas, posisi, dan keadaan (sedang jalan, bongkar muat, menunggu).
- Kenapa dibutuhkan: semua aturan soal (turun dulu baru naik, minimal 5 menit, berangkat bila tidak ada proses) bergantung pada keadaan bus saat ini.
- Cara kerja:
bus_init: di t = 0 bus sudah meninggalkan rental mobil, jadi disetBUS_MOVINGmenuju lokasi 1.bus_passenger_countmenjumlahkan isi list 4, 5, 6;bus_is_fullmembandingkannya dengan 20. Semua yang ada di bus dihitung, termasuk yang hanya lewat.bus_boardmemasukkan penumpang ke list tujuannya;bus_alightmengeluarkan penumpang pertama (FIFO) dari list tujuan lokasi sekarang. Keduanya memanggil hookstats_bus_count_changedsetiap jumlah di bus berubah.
- Kenapa kodenya begini: jumlah di bus dihitung dengan menjumlahkan tiga list, bukan variabel penghitung terpisah, supaya tidak mungkin tidak sinkron dengan isi list yang sebenarnya. Hook
stats_bus_count_changeddipanggil dibus_boarddanbus_alightkarena hanya di dua tempat itu jumlahnya berubah. - Uji diri: kenapa
bus.locationsaat bergerak berisi stop tujuan? (Supayaevent_bus_arrivallangsung tahu di mana bus akan tiba tanpa menyimpan variabel lain.)
Langkah 7: arrival (baris 380 sampai 407)
src/bus-sim.c baris 380-407
/* Event: passenger arrival. */
void schedule_next_arrival(int location)
{
event_schedule(sim_time + expon(locations[location].mean_interarrival,
STREAM_INTERARRIVAL(location)),
location);
}
void event_arrival(int location)
{
Passenger p;
/* Schedule the next arrival at this location. */
schedule_next_arrival(location);
/* The new passenger joins the queue. */
p = passenger_create(location);
passenger_file(&p, LIST_QUEUE(location));
/* The bus is still waiting at this stop, so the passenger can board. */
if (bus.status == BUS_IDLE && bus.location == location)
try_start_load();
}
- Kenapa ada: soal: orang datang di tiap lokasi dengan waktu antar-kedatangan eksponensial yang independen.
- Kenapa dibutuhkan: tanpa kedatangan tidak ada orang di sistem.
- Cara kerja (
event_arrival):- Jadwalkan kedatangan berikutnya di lokasi yang sama (
sim_time + expon(rata-rata, stream lokasi itu)), supaya aliran kedatangan tidak putus. - Buat orang baru dan masukkan ke antrian lokasi itu.
- Jika bus sedang
BUS_IDLEdi lokasi ini, panggiltry_start_load()agar orang itu langsung naik. Jika bus sedang memuat, orang itu menunggu dan diambil setelah muatan berjalan selesai.
- Jadwalkan kedatangan berikutnya di lokasi yang sama (
- Kenapa kodenya begini: kedatangan berikutnya dijadwalkan lebih dulu supaya aliran kedatangan tidak bergantung pada apa yang terjadi pada orang yang sekarang. Orang yang datang saat bus idle langsung dimuat karena bus masih ada di tempat dan ada kursi. Alternatifnya “snapshot” (hanya yang sudah antre saat bus tiba), yang lebih sederhana tetapi membuat bus mengabaikan orang yang berdiri di depannya selama menunggu 5 menit. Ini tafsiran kelompok, bukan keharusan soal.
- Uji diri: apa yang terjadi bila orang datang saat bus
BUS_UNLOADING? (Ia hanya masuk antrian. Setelah semua turun,event_end_unloadmemanggiltry_start_load, dan ia ikut naik bila bus belum penuh.)
Langkah 8: unloading (baris 439 sampai 471)
src/bus-sim.c baris 439-471
/* Event: unloading, one person at a time. */
int try_start_unload(void)
{
if (!bus_has_passenger_for(bus.location))
return 0;
bus.current = bus_alight(bus.location);
bus.status = BUS_UNLOADING;
event_schedule(sim_time + uniform(UNLOAD_TIME_MIN, UNLOAD_TIME_MAX, STREAM_UNLOAD),
EVENT_END_UNLOAD);
return 1;
}
void event_end_unload(void)
{
/* The passenger leaves the system here. */
stats_time_in_system(bus.current.origin, sim_time - bus.current.arrival_time);
/* Unload the next person; when nobody is left, start loading. */
if (try_start_unload())
return;
if (try_start_load())
return;
bus.status = BUS_IDLE;
try_depart();
}
- Kenapa ada: soal: orang diturunkan lebih dulu, FIFO, Uniform(16, 24) detik per orang.
- Kenapa dibutuhkan: tanpa ini penumpang tidak pernah sampai tujuan, sehingga statistik lama di sistem (f) tidak pernah tercatat.
- Cara kerja:
try_start_unload: jika tidak ada penumpang bertujuan stop ini, kembalikan 0. Jika ada, ambil yang pertama dari bus (bus_alight), setBUS_UNLOADING, jadwalkanEVENT_END_UNLOADsetelah waktu acak U(16, 24) detik, kembalikan 1.event_end_unload: orang itu keluar dari sistem, jadi catatsim_time - arrival_timelewatstats_time_in_system(asal, waktu). Lalu: masih ada yang turun? lanjut. Kalau tidak, mulai muat? lanjut. Kalau keduanya tidak, bus menjadiBUS_IDLEdan dicek apakah boleh berangkat (try_depart).
- Pola “try then return”:
if (try_start_unload()) return;berarti “kalau satu proses berhasil dimulai, selesai untuk sekarang; event berikutnya melanjutkan.” Pola sama dipakai di loading. - Kenapa kodenya begini: satu orang per event karena waktu turun tiap orang acak (U(16,24) detik), dan simulasi harus menunggu waktu itu sebelum orang berikutnya. Orang keluar dari list di awal bongkar supaya jumlah di bus langsung berkurang; alternatifnya di akhir bongkar (seperti
bus_sim.c), yang hanya mengubah rata-rata (c). Urutan “turun lalu naik” terjamin karenatry_start_loadbaru dipanggil setelah tak ada lagi yang turun. - Uji diri: kenapa penumpang keluar dari list bus di awal bongkar, bukan di akhir? (Jumlah di bus langsung berkurang saat orang mulai turun. Ini soal definisi, dan memengaruhi nilai statistik (c).)
Langkah 9: loading (baris 408 sampai 438)
src/bus-sim.c baris 408-438
/* Event: loading, one person at a time until the queue is empty or the bus is full. */
int try_start_load(void)
{
if (list_size[LIST_QUEUE(bus.location)] == 0 || bus_is_full())
return 0;
/* Take the first person from the queue; the delay in queue ends here. */
bus.current = passenger_remove_first(LIST_QUEUE(bus.location));
stats_queue_delay(bus.location, sim_time - bus.current.arrival_time);
bus.status = BUS_LOADING;
event_schedule(sim_time + uniform(LOAD_TIME_MIN, LOAD_TIME_MAX, STREAM_LOAD),
EVENT_END_LOAD);
return 1;
}
void event_end_load(void)
{
bus_board(&bus.current);
/* Load the next person if possible, otherwise the bus becomes idle. */
if (try_start_load())
return;
bus.status = BUS_IDLE;
try_depart();
}
- Kenapa ada: soal: orang dinaikkan sampai kapasitas, Uniform(15, 25) detik per orang.
- Kenapa dibutuhkan: tanpa ini tidak ada orang yang berpindah dari antrian ke bus, dan statistik (b), (c), (f) tidak bermakna.
- Cara kerja:
try_start_load: kalau antrian stop ini kosong atau bus penuh, kembalikan 0. Kalau tidak, ambil orang pertama dari antrian, catat delay-nya (sim_time - arrival_time) lewatstats_queue_delay, setBUS_LOADING, jadwalkanEVENT_END_LOADsetelah U(15, 25) detik, kembalikan 1.event_end_load: orang itu naik (bus_board). Kalau masih bisa memuat berikutnya, mulai lagi (muatan “mengular” satu per satu). Kalau tidak, bus menjadiBUS_IDLEdan dicektry_depart.
- Catatan: delay antrian dihitung sampai orang diambil dari antrian, bukan sampai selesai naik. Sama dengan keputusan di
bus_sim.ckita. - Kenapa kodenya begini: delay dicatat saat orang diambil dari antrian, bukan saat selesai naik, karena menunggu berakhir ketika pelayanan dimulai (pola yang sama dengan
jobshop.c). Rantaitry_start_load→event_end_load→try_start_loadmembuat proses “mengular” tanpa loopwhile, karena dalam event-scheduling waktu hanya bergerak lewat event. - Uji diri: apa yang menghentikan rantai muat? (Antrian kosong atau bus penuh;
try_start_loadmengembalikan 0.)
Langkah 10: bus tiba, batas 5 menit, berangkat (baris 472 sampai 526)
src/bus-sim.c baris 472-526
/* Event: bus arrival, 5-minute rule and departure. */
void event_bus_arrival(void)
{
bus.status = BUS_IDLE;
bus.arrival_time = sim_time;
bus.min_stop_reached = 0;
/* The bus stays at least 5 minutes. */
event_schedule(sim_time + MIN_STOP_TIME, EVENT_MIN_STOP_REACHED);
/* Unload first, then load. */
if (!try_start_unload())
try_start_load();
}
void event_min_stop_reached(void)
{
bus.min_stop_reached = 1;
try_depart();
}
/* Leave once 5 minutes have passed and no loading/unloading is in process.
Called when the 5 minutes run out and whenever the bus becomes idle. */
void try_depart(void)
{
if (bus.min_stop_reached && bus.status == BUS_IDLE)
bus_depart();
}
void bus_depart(void)
{
int here;
here = bus.location;
stats_bus_stop(here, sim_time - bus.arrival_time);
/* A loop runs from one car-rental departure to the next. */
if (here == LOC_CAR_RENTAL) {
stats_bus_loop(sim_time - bus.last_rental_departure);
bus.last_rental_departure = sim_time;
}
/* Drive to the next stop. */
event_schedule(sim_time + locations[here].travel_time_to_next, EVENT_BUS_ARRIVAL);
bus.location = locations[here].next_stop;
bus.status = BUS_MOVING;
bus.min_stop_reached = 0;
}
- Kenapa ada: soal: bus berhenti minimal 5 menit di tiap lokasi, dan bila setelah 5 menit tidak ada proses naik/turun, bus langsung berangkat.
- Kenapa dibutuhkan: ini pengatur siklus bus. Tanpanya bus tidak berpindah lokasi dan statistik (d) dan (e) kosong.
- Cara kerja:
event_bus_arrival:status = BUS_IDLE, catatarrival_time,min_stop_reached = 0. JadwalkanEVENT_MIN_STOP_REACHEDtepat 5 menit kemudian. Lalu “turun dulu, kalau tidak ada yang turun baru naik”.event_min_stop_reached:min_stop_reached = 1, lalutry_depart().try_depart: berangkat hanya jika dua syarat terpenuhi bersamaan: 5 menit sudah lewat dan busBUS_IDLE.bus_depart: catat lama berhenti (stats_bus_stop); kalau dari rental mobil, catat lama putaran (stats_bus_loop) dan simpan waktu berangkatnya; jadwalkanEVENT_BUS_ARRIVALdi lokasi berikutnya; ubahlocation,status = BUS_MOVING.
- Keberangkatan bukan event.
try_departdipanggil setiap kali bus menjadi idle dan saat 5 menit habis, jadi bus berangkat tepat saat syarat terpenuhi. - Bedanya dengan
bus_sim.ckita: kita menghitung ulangsim_time - dwell_startlalu membandingkannya dengan 5 menit, sehingga kena bug pembulatan desimal (sampai perluTIME_EPS). Di sini 5 menit ditandai oleh event yang mengisi flagmin_stop_reached, jadi tidak ada perbandingan angka desimal di batasnya dan bug itu tidak bisa muncul. - Kenapa kodenya begini: batas 5 menit ditandai oleh event
MIN_STOP_REACHEDyang mengisi flag, bukan dengan membandingkansim_time - arrival_time >= 5. Perbandingan desimal rawan salah di batas karena pembulatan, dan itu persis bug yang kita alami dibus_sim.c. Keberangkatan bukan event karena soal menyuruh bus berangkat “segera”; jadi cukup dicek setiap kali bus menjadi idle dan saat 5 menit habis (try_depart). Kedua syarat dicek bersamaan agar bus tidak pernah berangkat di tengah muat atau turun. - Uji diri: apa yang terjadi bila 5 menit habis saat bus masih memuat? (
try_departmelihat statusBUS_LOADING, tidak berangkat. Setelah muatan selesai dan bus jadiBUS_IDLE,try_departdipanggil lagi dan bus berangkat.)
Langkah 11: main, init_model, report (baris 158 sampai 256)
src/bus-sim.c baris 158-225
int main(void)
{
/* Open output file. */
outfile = fopen(OUTPUT_FILE, "w");
if (outfile == NULL)
fprintf(stderr, "Cannot open %s\n", OUTPUT_FILE);
/* Initialize simlib. */
init_simlib();
/* Set maxatr = max(maximum number of attributes per record, 4). */
maxatr = NUM_ATTRIBUTES;
/* Initialize the model. */
init_model();
/* Run the simulation until the end-simulation event occurs. */
do {
/* Determine the next event. */
timing();
trace_event();
/* Invoke the appropriate event function. */
switch (next_event_type) {
case EVENT_ARRIVAL_T1:
case EVENT_ARRIVAL_T2:
case EVENT_ARRIVAL_RENTAL:
event_arrival(next_event_type);
break;
case EVENT_BUS_ARRIVAL:
event_bus_arrival();
break;
case EVENT_END_UNLOAD:
event_end_unload();
break;
case EVENT_END_LOAD:
event_end_load();
break;
case EVENT_MIN_STOP_REACHED:
event_min_stop_reached();
break;
case EVENT_END_SIMULATION:
report(stdout);
if (outfile != NULL)
report(outfile);
break;
default:
fprintf(stderr, "Unknown event type %d at time %f\n",
next_event_type, sim_time);
return 1;
}
} while (next_event_type != EVENT_END_SIMULATION);
if (outfile != NULL)
fclose(outfile);
return 0;
}
src/bus-sim.c baris 226-256
void init_model(void)
{
int loc;
location_init();
bus_init();
stats_init();
/* Schedule the first arrival at each location. */
for (loc = 1; loc <= NUM_LOCATIONS; ++loc)
schedule_next_arrival(loc);
/* The bus leaves the car rental at t = 0 without stopping, so its first
event is the arrival at terminal 1. */
event_schedule(locations[LOC_CAR_RENTAL].travel_time_to_next, EVENT_BUS_ARRIVAL);
/* Schedule the end of the simulation. */
event_schedule(SIM_LENGTH, EVENT_END_SIMULATION);
}
void report(FILE *out)
{
fprintf(out, "Simulation Results\n");
fprintf(out, "------------------\n");
fprintf(out, "Run length: %.0f minutes\n", SIM_LENGTH);
stats_report(out);
}
- Kenapa ada: merakit semua bagian menjadi satu program yang bisa dijalankan.
- Kenapa dibutuhkan: tanpa loop utama, fungsi-fungsi event ada tetapi tidak pernah dipanggil.
- Cara kerja:
- Buka file output,
init_simlib(), lalumaxatr = NUM_ATTRIBUTES(urutan sesuai buku). init_model: siapkan lokasi, bus, dan statistik; jadwalkan kedatangan pertama di tiga lokasi; jadwalkan bus tiba di lokasi 1 setelah waktu tempuh dari rental mobil (bus dianggap sudah berangkat di t = 0); jadwalkan akhir simulasi diSIM_LENGTH(4800 menit).- Loop:
timing()mengambil event terdekat dan memajukansim_time;trace_event(opsional) mencetak keadaan;switchmemanggil fungsi event yang sesuai; berhenti setelah event akhir simulasi. - Pada event akhir,
reportdipanggil dua kali: ke layar dan ke file.reportmencetak judul lalu memanggilstats_report.
- Buka file output,
- Kenapa kodenya begini: kerangka
init_simlib, jadwalkan event awal, lalu looptiming()denganswitchadalah pola baku SIMLIB (lihatjobshop.c). Bus tidak diberi event “tiba di lokasi 3” di t = 0; ia langsung dijadwalkan tiba di lokasi 1 karena soal bilang bus berangkat segera dan antrian masih kosong.reportdipanggil dari event akhir simulasi supaya rata-rata terhadap waktu dihitung tepat sampai menit 4800. - Uji diri: kenapa kedatangan berikutnya dijadwalkan dari dalam
event_arrival, bukan semuanya di awal? (Waktu antar-kedatangan acak, jumlah kedatangan dalam 80 jam tidak diketahui di awal, dan satu kedatangan menentukan kapan yang berikutnya.)
Langkah 12: statistik (baris 527 sampai 644)
src/bus-sim.c baris 527-644
/* Statistics (a)-(f). The hooks pass each value to SIMLIB; stats_report()
reads the results at the end of the run. All times are in minutes. */
void stats_init(void)
{
/* The bus starts empty. */
timest(0.0, TIMEST_NUM_ON_BUS);
}
/* (b) delay in queue */
void stats_queue_delay(int location, double delay)
{
sampst(delay, SAMPST_QUEUE_DELAY(location));
}
/* (c) number on the bus */
void stats_bus_count_changed(int passengers_on_bus)
{
timest((double) passengers_on_bus, TIMEST_NUM_ON_BUS);
}
/* (d) time the bus is stopped */
void stats_bus_stop(int location, double stop_time)
{
sampst(stop_time, SAMPST_STOP_TIME(location));
}
/* (e) loop time */
void stats_bus_loop(double loop_time)
{
sampst(loop_time, SAMPST_LOOP_TIME);
}
/* (f) time in system by arrival location */
void stats_time_in_system(int origin, double time)
{
sampst(time, SAMPST_TIME_IN_SYSTEM(origin));
}
/* One table row. A negative value is printed as "-" (not asked or no data).
max_is_count = 1 prints the maximum as a whole number (people). */
void stats_report_line(FILE *out, const char *label, double avg, double max,
double min, int max_is_count)
{
char avg_text[32], max_text[32], min_text[32];
if (avg < 0.0) sprintf(avg_text, "-"); else sprintf(avg_text, "%.3f", avg);
if (max < 0.0) sprintf(max_text, "-");
else sprintf(max_text, max_is_count ? "%.0f" : "%.3f", max);
if (min < 0.0) sprintf(min_text, "-"); else sprintf(min_text, "%.3f", min);
fprintf(out, "%-42s | %9s | %9s | %9s\n", label, avg_text, max_text, min_text);
}
/* A row from a sampst variable; show_min = 0 prints "-" for the minimum. */
void stats_report_sampst(FILE *out, const char *label, int variable, int show_min)
{
double count, avg, max, min;
/* sampst(0.0, -variable) reports; copy transfer[] before the next call. */
sampst(0.0, -variable);
avg = transfer[1];
count = transfer[2];
max = transfer[3];
min = transfer[4];
if (count == 0.0)
stats_report_line(out, label, -1.0, -1.0, -1.0, 0);
else
stats_report_line(out, label, avg, max, show_min ? min : -1.0, 0);
}
/* Prints (a)-(f) as one table. */
void stats_report(FILE *out)
{
int loc;
char label[64];
double avg, max;
fprintf(out, "Locations: 1 = %s, 2 = %s, 3 = %s\n",
locations[1].name, locations[2].name, locations[3].name);
fprintf(out, "All times are in minutes\n\n");
fprintf(out, "%-42s | %9s | %9s | %9s\n", "", "Average", "Max", "Min");
for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
sprintf(label, "%sNumber in queue at location %d", loc == 1 ? "[a] " : " ", loc);
filest(LIST_QUEUE(loc));
avg = transfer[1];
max = transfer[2];
stats_report_line(out, label, avg, max, -1.0, 1);
}
for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
sprintf(label, "%sDelay in queue at location %d", loc == 1 ? "[b] " : " ", loc);
stats_report_sampst(out, label, SAMPST_QUEUE_DELAY(loc), 0);
}
timest(0.0, -TIMEST_NUM_ON_BUS);
avg = transfer[1];
max = transfer[2];
stats_report_line(out, "[c] Number on the bus", avg, max, -1.0, 1);
for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
sprintf(label, "%sBus stop time at location %d", loc == 1 ? "[d] " : " ", loc);
stats_report_sampst(out, label, SAMPST_STOP_TIME(loc), 1);
}
stats_report_sampst(out, "[e] Bus loop time", SAMPST_LOOP_TIME, 1);
for (loc = 1; loc <= NUM_LOCATIONS; ++loc) {
sprintf(label, "%sTime in system, arrived at location %d",
loc == 1 ? "[f] " : " ", loc);
stats_report_sampst(out, label, SAMPST_TIME_IN_SYSTEM(loc), 1);
}
}
- Kenapa ada: soal meminta enam kelompok statistik (a) sampai (f) di akhir simulasi: rata-rata dan maksimum jumlah di antrian, delay antrian, jumlah di bus, lama bus berhenti, lama putaran, dan lama orang di sistem.
- Kenapa dibutuhkan: tanpa ini program jalan tetapi tidak menghasilkan jawaban apa pun. Seluruh tujuan tugas ini adalah angka-angka tersebut.
- Alurnya tiga tahap. (1) Model memanggil hook pada saat yang benar. (2) Hook menitipkan angka ke SIMLIB. (3) Di akhir,
stats_reportmembaca kembali dan mencetaknya. Statistik tidak mengubah keadaan model.
Peta hook:
| Soal | Hook | Isi hook | Dipanggil oleh |
|---|---|---|---|
| (a) | tidak ada | SIMLIB melacak list 1-3 sendiri | |
| (b) | stats_queue_delay(loc, d) | sampst(d, SAMPST_QUEUE_DELAY(loc)) | try_start_load, saat orang diambil dari antrian |
| (c) | stats_bus_count_changed(n) | timest(n, TIMEST_NUM_ON_BUS) | bus_board dan bus_alight, tiap jumlah di bus berubah |
| (d) | stats_bus_stop(loc, t) | sampst(t, SAMPST_STOP_TIME(loc)) | bus_depart |
| (e) | stats_bus_loop(t) | sampst(t, SAMPST_LOOP_TIME) | bus_depart, hanya saat berangkat dari rental |
| (f) | stats_time_in_system(o, t) | sampst(t, SAMPST_TIME_IN_SYSTEM(o)) | event_end_unload, saat orang selesai turun |
Bagian-bagiannya:
stats_init:timest(0.0, TIMEST_NUM_ON_BUS)menetapkan level jumlah-di-bus mulai dari 0 pada t = 0. Rata-rata dan maksimum (c) tetap benar tanpa baris ini, tetapi nilai minimumnya akan salah (terisi 1, nilai pertama yang tercatat). Minimum (c) tidak dicetak, jadi ini soal kerapian.- Lima hook: masing-masing satu baris panggilan SIMLIB.
stats_bus_count_changedmemakaitimest, bukansampst, karena yang dicatat adalah level yang bertahan sekian lama. stats_report_line: mencetak satu baris tabel: label, rata-rata, maks, dan min, dipisah tanda|. Nilai negatif dicetak sebagai tanda-(dipakai untuk kolom yang tidak diminta soal, atau bila belum ada data).max_is_count = 1mencetak maks sebagai bilangan bulat (untuk jumlah orang). Semua angka dalam menit.stats_report_sampst: menyiapkan satu baris dari variabelsampst.sampst(0.0, -variable)(nilai negatif berarti minta laporan), lalu hasilnya segera disalin daritransfer[1..4](rata-rata, jumlah, maks, min). Jika jumlahnya 0, SIMLIB mengisi maks dan min dengan angka sentinel1e30, jadi baris dicetak dengan tanda-. Parametershow_minbernilai 0 untuk (b) karena soal hanya meminta rata-rata dan maksimum delay.stats_report: mencetak seluruh tabel (a) sampai (f). (a) memakaifilest(LIST_QUEUE(loc)); (c) memakaitimest(0.0, -TIMEST_NUM_ON_BUS); sisanya memakaistats_report_sampst. Dipanggil dua kali olehmain(ke layar dan ke file); aman karena membaca laporan tidak mengubah angka.- Satuan dan bentuk: semua angka sudah menit, jadi tidak ada konversi saat mencetak. Laporannya satu tabel; soalnya tidak ditulis ulang, hanya label baris yang singkat.
- Kenapa (a) tanpa hook, (c) dengan hook: list antrian adalah list SIMLIB sehingga panjangnya dilacak otomatis. Jumlah di bus adalah gabungan tiga list (4 sampai 6), bukan satu list, jadi model memberi tahu lewat hook tiap jumlahnya berubah.
- Kenapa kodenya begini: statistik dipisah dari model lewat hook agar model tidak perlu tahu cara pencatatan; mengubah statistik tidak berisiko merusak logika bus.
sampstdipakai untuk nilai per kejadian dantimestuntuk level yang berubah terhadap waktu, karena rata-ratanya dihitung berbeda. (a) memakaifilestkarena SIMLIB sudah melacak list sendiri. Tabel dicetak lewat fungsi satu baris agar formatnya konsisten dan kasus belum ada data (n = 0) ditangani di satu tempat. - Uji diri: kenapa nilai hasil harus langsung disalin dari
transfer[]? (Panggilan SIMLIB berikutnya menimpanya.)
Langkah 13: trace (baris 645 sampai 683)
src/bus-sim.c baris 645-683
/* Trace, printed only when ENABLE_TRACE is 1. */
const char *event_name(int type)
{
switch (type) {
case EVENT_ARRIVAL_T1: return "ARRIVAL_T1";
case EVENT_ARRIVAL_T2: return "ARRIVAL_T2";
case EVENT_ARRIVAL_RENTAL: return "ARRIVAL_RENTAL";
case EVENT_BUS_ARRIVAL: return "BUS_ARRIVAL";
case EVENT_END_UNLOAD: return "END_UNLOAD";
case EVENT_END_LOAD: return "END_LOAD";
case EVENT_MIN_STOP_REACHED: return "MIN_STOP_REACHED";
case EVENT_END_SIMULATION: return "END_SIMULATION";
}
return "UNKNOWN";
}
const char *bus_status_name(BusStatus status)
{
switch (status) {
case BUS_MOVING: return "MOVING";
case BUS_IDLE: return "IDLE";
case BUS_UNLOADING: return "UNLOADING";
case BUS_LOADING: return "LOADING";
}
return "?";
}
void trace_event(void)
{
#if ENABLE_TRACE
printf("%9.3f %-16s bus@%d %-9s on_bus=%2d q1=%3d q2=%3d q3=%3d\n",
sim_time, event_name(next_event_type), bus.location,
bus_status_name(bus.status), bus_passenger_count(),
list_size[LIST_QUEUE(LOC_TERMINAL_1)],
list_size[LIST_QUEUE(LOC_TERMINAL_2)],
list_size[LIST_QUEUE(LOC_CAR_RENTAL)]);
#endif
}- Kenapa ada: untuk memeriksa apakah bus dan antrian bergerak sesuai kronologi yang benar (teknik “trace”).
- Kenapa dibutuhkan: tanpa melihat jalannya event, bug seperti bus macet hanya ketahuan dari angka akhir yang aneh.
- Cara kerja: bila
ENABLE_TRACEbernilai 1, setiap event mencetak satu baris: waktu (menit), nama event, lokasi dan status bus, jumlah di bus, dan panjang tiga antrian. Keadaan yang tampil adalah keadaan sebelum event diproses. Bila 0,trace_eventtidak melakukan apa-apa.
Langkah 14: Makefile
Makefile
# Car-rental bus simulation (Law & Kelton, Problem 2.38) - C + SIMLIB.
#
# make build bus-sim
# make run build and run the 80-hour simulation (report also in bus-sim.out)
# make trace build bus-sim_trace (event trace, first 60 simulated minutes) and run it
# make clean remove build output
CC = gcc
CFLAGS = -std=c89 -Wall -Wextra -pedantic -O2 -Ilib/simlib
LIBFLAGS = -O2 -Ilib/simlib
LDLIBS = -lm
ifeq ($(OS),Windows_NT)
EXE = .exe
else
EXE =
endif
TARGET = bus-sim$(EXE)
TRACE_TARGET = bus-sim_trace$(EXE)
TRACE_MINUTES = 60.0
SRC = src/bus-sim.c
SIMLIB_HDR = lib/simlib/simlib.h lib/simlib/simlibdefs.h
SIMLIB_OBJ = simlib.o
.PHONY: all run trace clean
all: $(TARGET)
$(TARGET): $(SRC) $(SIMLIB_HDR) $(SIMLIB_OBJ)
$(CC) $(CFLAGS) -o $@ $(SRC) $(SIMLIB_OBJ) $(LDLIBS)
$(TRACE_TARGET): $(SRC) $(SIMLIB_HDR) $(SIMLIB_OBJ)
$(CC) $(CFLAGS) -DENABLE_TRACE=1 -DSIM_LENGTH=$(TRACE_MINUTES) -o $@ $(SRC) $(SIMLIB_OBJ) $(LDLIBS)
# SIMLIB is textbook code: compiled separately, without our strict warning flags.
$(SIMLIB_OBJ): lib/simlib/simlib.c lib/simlib/simlibdefs.h
$(CC) $(LIBFLAGS) -c lib/simlib/simlib.c -o $@
run: $(TARGET)
./$(TARGET)
trace: $(TRACE_TARGET)
./$(TRACE_TARGET)
clean:
$(RM) $(TARGET) $(TRACE_TARGET) $(SIMLIB_OBJ) bus-sim.out- Kenapa ada: menyingkat perintah compile yang panjang.
- Cara kerja:
makemembangunbus-sim;make runmembangun dan menjalankan 4800 menit;make tracemembangun versi dengan trace dan hanya 60 menit simulasi;make cleanmenghapus hasil build. - Dua hal yang perlu diketahui: (1) kode buatan temanmu di-compile dengan
-std=c89 -Wall -Wextra -pedantic(tidak boleh komentar//atau deklarasi variabel di tengah blok); (2) SIMLIB di-compile terpisah tanpa aturan ketat itu, karena itu kode buku. Di Windows butuhmingw32-make, atau jalankan lewat WSL.
Bagian 3: menelusuri hasil nyata (make trace, 60 menit simulasi)
Setiap baris: waktu (menit), event, posisi dan status bus, jumlah di bus, panjang antrian 1/2/3. Keadaan yang tampil adalah sebelum event diproses. Cuplikan dari make trace:
9.000 BUS_ARRIVAL bus@1 MOVING on_bus= 0 q1= 2 q2= 0 q3= 6
9.338 END_LOAD bus@1 LOADING on_bus= 0 q1= 1 q2= 0 q3= 6
9.596 ARRIVAL_T1 bus@1 LOADING on_bus= 1 q1= 0 q2= 0 q3= 6
9.753 END_LOAD bus@1 LOADING on_bus= 1 q1= 1 q2= 0 q3= 6
10.004 ARRIVAL_T2 bus@1 LOADING on_bus= 2 q1= 0 q2= 0 q3= 6
10.166 END_LOAD bus@1 LOADING on_bus= 2 q1= 0 q2= 1 q3= 6
13.709 ARRIVAL_T1 bus@1 IDLE on_bus= 3 q1= 0 q2= 1 q3= 6
14.000 MIN_STOP_REACHED bus@1 LOADING on_bus= 3 q1= 0 q2= 1 q3= 6
14.072 END_LOAD bus@1 LOADING on_bus= 3 q1= 0 q2= 1 q3= 6
15.927 ARRIVAL_RENTAL bus@2 MOVING on_bus= 4 q1= 0 q2= 1 q3= 6
16.072 BUS_ARRIVAL bus@2 MOVING on_bus= 4 q1= 0 q2= 1 q3= 7
...
21.072 MIN_STOP_REACHED bus@2 IDLE on_bus= 6 q1= 2 q2= 0 q3= 8
...
30.072 BUS_ARRIVAL bus@3 MOVING on_bus= 6 q1= 5 q2= 2 q3= 15
30.412 END_UNLOAD bus@3 UNLOADING on_bus= 5 q1= 5 q2= 2 q3= 15
30.707 END_UNLOAD bus@3 UNLOADING on_bus= 4 q1= 5 q2= 2 q3= 15
Cara membacanya:
- 9,000 bus tiba di lokasi 1. Waktu tempuh 3 → 1 adalah 4,5/30 × 60 = 9 menit, cocok. Antrian 1 berisi 2 orang. Tidak ada yang turun (bus kosong), jadi bus langsung memuat;
END_LOADpertama di 9,338. - 9,596 satu orang datang di terminal 1 saat bus sedang memuat. Ia hanya masuk antrian dan diambil setelah muatan berjalan selesai.
- 13,709 orang datang saat bus
IDLEdi lokasi 1 (antrian sudah kosong, bus menunggu 5 menit). Ia langsung dimuat, terlihat dari baris berikutnya berstatusLOADING. - 14,000 = 9,000 + 5 menit. Batas 5 menit tercapai, tetapi bus masih
LOADING, jadi tidak berangkat. Baru setelahEND_LOADdi 14,072 bus berangkat (baris berikutnya bus sudah menuju lokasi 2). - 16,072 bus tiba di lokasi 2: 14,072 + 2 menit (1 mil). Tepat 5 menit kemudian, 21,072, bus
IDLEdan langsung berangkat. - 30,072 bus tiba di lokasi 3: 21,072 + 9 menit.
on_bus6, antrian rental 15 orang. Orang bertujuan lokasi 3 turun satu per satu (on_bus5, 4, …). Jumlah di bus berkurang di awal tiap bongkar, itu sebabnyaon_bussudah turun saatEND_UNLOADtercetak.
Latihan: ambil 3 baris lain dari make trace dan jelaskan dengan cara yang sama.
Bagian 4: mesin status bus
| Dari | Pemicu | Ke |
|---|---|---|
| (awal) | t = 0, bus meninggalkan rental mobil | MOVING |
| MOVING | bus tiba, ada yang turun di sini | UNLOADING |
| MOVING | bus tiba, tak ada yang turun, antrian ada, belum penuh | LOADING |
| MOVING | bus tiba, tak ada yang dikerjakan | IDLE |
| UNLOADING | selesai turun, masih ada yang turun | UNLOADING |
| UNLOADING | selesai turun, antrian ada, belum penuh | LOADING |
| UNLOADING | selesai turun, tak ada yang dikerjakan | IDLE |
| LOADING | selesai naik, antrian ada, belum penuh | LOADING |
| LOADING | selesai naik, tak ada yang dikerjakan | IDLE |
| IDLE | orang datang saat bus menunggu, belum penuh | LOADING |
| IDLE | 5 menit tercapai dan idle | MOVING |
Hal yang selalu harus benar (bisa dipakai untuk menguji):
- Jumlah di bus selalu antara 0 dan 20.
- Saat
MOVING, tidak adaEND_LOAD/END_UNLOADyang tertunda. - Setiap stop berlangsung minimal 5 menit; bus berangkat hanya bila
min_stop_reached == 1dan statusIDLE. - Tidak ada penumpang di bus yang tujuannya sama dengan asalnya.
- Begitu pemuatan dimulai di suatu stop, tidak ada lagi yang perlu diturunkan di situ.
Bagian 5: perbandingan dengan bus_sim.c kita
| Hal | bus_sim.c (kita) | bus-sim.c (temanmu) |
|---|---|---|
| Struktur | satu berkas, ±335 baris | satu berkas, 683 baris, bagian per entitas dan event |
| Satuan waktu | jam | menit |
| Data penumpang | transfer[3..5] | struct Passenger, disalin segera dari transfer[] |
| Aturan 5 menit | bandingkan waktu, perlu TIME_EPS dan flag depart_check_scheduled | event MIN_STOP_REACHED yang mengisi flag; tak ada perbandingan desimal |
| Keberangkatan | check_departure dipanggil dari beberapa tempat | try_depart dipanggil saat idle dan saat 5 menit habis |
| Statistik | sudah lengkap di report() | sudah diisi: hook ke SIMLIB dan stats_report |
| Nomor list, stream, rute | sama | sama |
| Tafsir soal (rute 4,5/1/4,5, muat dinamis, tujuan diundi saat tiba) | ya | ya |
| Standar C | gnu11 | C89 ketat (-pedantic) |
Logikanya sama. Sebelum modul disatukan, repo ini punya versi modular yang jam-based; trace versi modular itu dan trace versi satu file ini identik (urutan event, status bus, jumlah di bus, panjang antrian), hanya waktunya berbeda skala (angka versi lama × 60 = menit). Jadi penyatuan file tidak mengubah perilaku model.
Bagian 6: statistik (a) sampai (f): hasil nyata dan pemeriksaan kewajaran
Hasil make run (4800 menit simulasi, waktu jalan sekitar 0,06 detik), semua waktu dalam menit:
Simulation Results
------------------
Run length: 4800 minutes
Locations: 1 = Air terminal 1, 2 = Air terminal 2, 3 = Car rental
All times are in minutes
| Average | Max | Min
[a] Number in queue at location 1 | 7.368 | 27 | -
Number in queue at location 2 | 4.888 | 17 | -
Number in queue at location 3 | 9.321 | 29 | -
[b] Delay in queue at location 1 | 31.414 | 91.934 | -
Delay in queue at location 2 | 29.820 | 130.860 | -
Delay in queue at location 3 | 24.130 | 67.485 | -
[c] Number on the bus | 13.593 | 20 | -
[d] Bus stop time at location 1 | 6.698 | 10.171 | 5.000
Bus stop time at location 2 | 5.473 | 9.287 | 5.000
Bus stop time at location 3 | 11.402 | 13.743 | 5.572
[e] Bus loop time | 43.573 | 48.069 | 35.715
[f] Time in system, arrived at location 1 | 51.927 | 112.294 | 17.738
Time in system, arrived at location 2 | 45.544 | 147.004 | 13.929
Time in system, arrived at location 3 | 41.724 | 88.289 | 11.532Cara membaca:
- (a) terminal 1: rata-rata 7,368 orang menunggu, paling banyak 27.
- (b) orang di terminal 1 rata-rata menunggu 31,4 menit, paling lama 91,9 menit.
- (c) rata-rata 13,6 dari 20 kursi terisi; bus pernah penuh (maks 20).
- (d) bus berhenti paling singkat 5,000 menit, tepat batas bawah dari soal.
- (e) 110 putaran, rata-rata 43,6 menit.
- (f) orang dari terminal 1 rata-rata 51,9 menit di sistem, dari antrian sampai turun.
Pemeriksaan kewajaran
| Pemeriksaan | Batas atau harapan | Hasil | Lolos? |
|---|---|---|---|
| maks (c) | tidak lebih dari 20 | 20 | ya |
| min (d) | tidak kurang dari 5,000 menit | 5,000 | ya |
| min (e) | tidak kurang dari 35 menit (20 menit jalan + 3 × 5 menit berhenti) | 35,715 | ya |
| jumlah putaran | 4800 ÷ rata-rata putaran ≈ 110 | 4800 ÷ 43,573 = 110,2; hasil 110 | ya |
| Hukum Little terminal 1 | (14 ÷ 60) orang per menit × 31,414 menit | 7,33 vs (a) 7,368 | ya |
| Hukum Little rental | (24 ÷ 60) orang per menit × 24,130 menit | 9,65 vs (a) 9,321 | ya, mendekati |
Daftar uji dari DESIGN.md bagian 7, sudah dicek:
- Compile tanpa warning dengan
-std=c89 -Wall -Wextra -pedantic(0 warning). - Bus tiba di lokasi 1 pertama kali pada t = 9 menit (baris pertama trace di Bagian 3).
- Bus mengunjungi stop berurutan 1, 2, 3 (terlihat di trace).
- Tiap stop minimal 5 menit (min (d) = 5,000).
- Bus tidak berangkat saat memuat (trace pada t = 14,000).
- Orang yang datang saat bus menunggu idle langsung dimuat (trace pada t = 13,709).
- Jumlah di bus tak pernah lebih dari 20 (maks (c)).
- Kedatangan sesuai laju: terminal 1 mencatat 1120 orang naik bus (14 × 80), terminal 2 mencatat 784, rental 1854 (jumlah ini tidak dicetak di tabel; dicek dengan menambah kolom
nsementara). - Tiap putaran minimal 35 menit (min (e) = 35,715).
Dibandingkan dengan bus_sim.c kita
Statistik (a), (b), (d), (e), (f) identik dengan hasil bus_sim.c (angka waktu bus_sim.c dikali 60). Dua program yang ditulis terpisah, dengan struktur kode berbeda, menghasilkan angka yang sama untuk lima dari enam statistik. Itu bukti kuat bahwa keduanya mengimplementasikan aturan yang sama dengan benar.
Satu-satunya beda ada di (c): 13,593 di sini vs 13,853 di bus_sim.c. Penyebabnya definisi, bukan bug. Di sini penumpang dihitung keluar dari bus di awal bongkar (bus_alight), sedangkan di bus_sim.c ia keluar di akhir bongkar, jadi rata-ratanya sedikit lebih tinggi. Soal tidak menentukan mana yang benar. Pilih satu dan nyatakan di deskripsi.
Jebakan yang sengaja dihindari
- Baca
transfer[]segera. Setiap panggilan SIMLIB bisa menimpanya, jadi nilai disalin ke variabel lokal tepat setelah panggilan. - Dua layout hasil berbeda.
sampstmemberi 4 nilai (rata-rata, n, maks, min);timestdanfilestmemberi 3 (rata-rata, maks, min). Tertukar indeks memberi angka salah tanpa error. - Observasi kosong.
sampstmemberi±1e30bila belum ada data; dicegah dengan pengecekancount == 0. - Yang belum selesai di t = 4800 tidak dihitung. Orang yang masih di antrian atau di bus tidak ikut (b) dan (f), dan putaran yang sedang berjalan tidak ikut (e).
Bagian 7: apa yang boleh dan tidak boleh kamu klaim
Boleh diklaim:
- Repo ini di-compile tanpa warning dan jalan 4800 menit sampai selesai, dengan statistik yang sudah diisi.
- Semua pemeriksaan kewajaran di Bagian 6 lolos.
- Hasil (a), (b), (d), (e), (f) sama dengan implementasi satu berkas kita.
- Rute 4,5 / 1 / 4,5 mil, penomoran list dan stream, dan aturan naik-turun-5 menit sesuai tafsiran yang kita sepakati.
- Aturan 5 menit memakai event penanda, bukan perbandingan waktu desimal.
- Penyatuan ke satu file tidak mengubah perilaku model (trace identik dengan versi modular).
Jangan diklaim:
- “Model ini karya saya.” Modelnya ditulis temanmu;
lib/simlib/dari buku. Bagianmu: fungsi statistik, pengecekan, output, dan penjelasan. Fungsi statistik itu ditulis dengan bantuan asisten, jadi jelaskan hanya yang benar-benar kamu pahami. - “Hasilnya benar karena sama dengan
bus_sim.c.” Dua program yang cocok mendukung kebenaran, tetapi keduanya memakai tafsir soal yang sama (rute, muat dinamis, tujuan diundi saat tiba). Jika tafsirnya keliru, keduanya keliru bersama. - “Jarak rute pasti sesuai maksud soal.” Itu tafsiran atas Gambar 2.72, bukan angka tertulis di soal.
- “Hasil satu kali jalan ini representatif.” Itu satu replikasi dengan seed bawaan SIMLIB.
- “(c) pasti 13,593.” Angka itu bergantung pada definisi kapan orang dianggap keluar dari bus.
Status git: perubahan statistik di src/bus-sim.c baru ada di salinan lokal (belum di-commit dan belum di-push). Pada riwayat repo, stats.c versi modular pernah ditambahkan lalu dihapus oleh commit “delete stats”. Beri tahu temanmu sebelum berkas ini digabung, supaya tidak ada dua orang mengubah fungsi stats_* yang sama.
Bagian 8: soal latihan
Jawab dengan kata-katamu, lalu cek ke langkah yang disebut.
- Kenapa semua angka soal dikumpulkan di bagian atas dan satuannya menit? (Langkah 1)
- Kenapa
passenger_remove_firstlangsung menyalin data ke struct? (Langkah 4) - Apa isi list 1-3 dan list 4-6? Kenapa list bus dibagi menurut tujuan? (Langkah 2, 6)
- Jelaskan perbedaan
BUS_MOVING,BUS_IDLE,BUS_UNLOADING,BUS_LOADING. (Langkah 3, Bagian 4) - Apa yang terjadi bila orang datang saat bus
IDLEdi lokasinya? Bagaimana bila bus sedangLOADING? (Langkah 7) - Kenapa bus tidak berangkat tepat di menit ke-5 bila masih memuat? Kapan akhirnya berangkat? (Langkah 10, Bagian 3 baris 14,000)
- Kenapa keberangkatan bukan event tersendiri? (Langkah 10)
- Kenapa repo ini tidak kena bug bus macet yang kita alami di
bus_sim.c? (Langkah 10, Bagian 5) - Bagaimana laju 14 orang per jam menjadi 4,29 menit antar-kedatangan di kode? (Langkah 5)
- Apa beda
sampstdantimest? Beri satu contoh statistik untuk masing-masing. (Bagian 0A) - Kenapa statistik (a) tidak butuh hook, sedangkan (c) butuh? (Bagian 6)
- Apa arti
sampst(0.0, -var)? Kenapa nilainya negatif? (Bagian 0A) - Kenapa nilai (c) bisa berbeda antara versi ini dan
bus_sim.c? (Bagian 6) - Jika dosen bertanya “bagaimana kamu tahu angkanya benar?”, sebutkan tiga pemeriksaan yang kamu lakukan. (Bagian 6)
- Tulis ulang satu langkah dari Bagian 2 dengan format: “[bagian] ada karena [kalimat soal]. Tanpanya [akibat]. Cara kerjanya: [3-5 langkah].”
Bagian 9: cara menjalankan dari nol
Butuh gcc dan GNU make. Di Windows: mingw32-make (MSYS2), atau jalankan lewat WSL.
cd tubes1-simlib-bus
make # build bus-sim
make run # jalankan 4800 menit; hasil tercetak dan tertulis ke bus-sim.out
make trace # versi trace: 60 menit simulasi, satu baris per event
make clean # hapus hasil build (termasuk bus-sim.out)Tanpa make:
gcc -Ilib/simlib -o bus-sim src/bus-sim.c lib/simlib/simlib.c -lmCatatan: bus-sim.out ada di .gitignore, jadi tidak tersimpan di git. Buat sendiri dengan make run
dan simpan salinannya sebagai output yang didemokan. Lama simulasi bisa diubah lewat
-DSIM_LENGTH=... (dalam menit) pada baris compile.
Bagian J: pengumpulan dan persiapan demo
Yang dikumpulkan (dalam satu PDF)
| Item | Sumber | Catatan |
|---|---|---|
| Deskripsi masalah, satu halaman | Bagian B (tabel keputusan) sebagai bahan; tulis ringkas dengan kata-katamu | tulis semua keputusan tafsir; definisi (c) ikut ditulis |
| Source code | src/bus-sim.c (dan sebut bahwa lib/simlib/ adalah SIMLIB dari buku) | harus sama persis dengan yang didemokan |
| Output | hasil make run, yaitu bus-sim.out | jalankan ulang bila kode berubah, supaya sama |
Sebelum demo
- Pastikan versi kode yang dikumpulkan dan yang didemokan sama; jalankan
make rundari kode itu dan simpanbus-sim.out. - Cek tidak ada warning saat compile.
- Cocokkan angka dengan tabel di Bagian 6 (harus identik karena seed bawaan SIMLIB tetap).
- Latih menjelaskan tiap bagian kode dengan format “ada karena …, tanpanya …, cara kerjanya …” (soal latihan di Bagian 8).
Urutan demo yang disarankan
| Langkah | Yang ditunjukkan | Kata kunci penjelasan |
|---|---|---|
| 1 | soal dan rute (Gambar 2.72) | jarak 4,5 / 1 / 4,5 mil, hasil tafsiran kita |
| 2 | struktur bus-sim.c | parameter, entitas, event, statistik, trace |
| 3 | make run dan tabel hasil | satuan menit; (a) sampai (f) |
| 4 | make trace | bukti aturan 5 menit dan urutan 1, 2, 3 |
| 5 | pemeriksaan kewajaran | min (d) = 5,000; min (e) ≥ 35; Hukum Little |
| 6 | batas | satu kali jalan, tafsiran kelompok |
Pembagian peran yang jujur
| Bagian | Pemilik |
|---|---|
Model di bus-sim.c (selain statistik) | temanmu |
| Fungsi statistik, pemeriksaan, output, deskripsi | kamu (statistik ditulis dengan bantuan asisten) |
lib/simlib/ | buku Law & Kelton |
Dosen bisa bertanya ke siapa saja, jadi kalian berdua harus bisa menjelaskan seluruh program. Jelaskan hanya yang benar-benar dipahami, dan jangan mengaku menulis bagian yang bukan milikmu.
Bagian K: pertanyaan yang mungkin ditanyakan dosen
| Pertanyaan | Jawaban singkat | Rujukan |
|---|---|---|
| Kenapa memakai simulasi, bukan rumus antrian? | Ada tiga antrian, satu pelayan yang berpindah, kapasitas, minimal 5 menit, dan waktu layanan acak; tidak ada rumus tertutup untuk gabungan itu. | Bagian A |
| Apa itu event-driven? | Jam simulasi melompat dari satu kejadian ke kejadian berikutnya, bukan berdetak per detik. | Bagian A |
| Bagaimana jarak antar-halte didapat? | Dijumlahkan dari ruas jalan di Gambar 2.72 menurut arah berlawanan jarum jam: 4,5 / 1 / 4,5 mil. Itu tafsiran kami. | Bagian B |
| Kenapa tujuan penumpang diundi saat datang? | Stream 6 dinamai “destination of an arrival”, dan dengan begitu stream dipakai tepat sekali per orang. | Langkah 4 |
| Bagaimana aturan 5 menit dibuat? | Event MIN_STOP_REACHED mengisi flag; bus berangkat saat flag menyala dan tidak ada proses naik/turun. | Langkah 10 |
| Kenapa satuannya menit? | Data soal campur jam, detik, dan mph; menit membuat angka seperti 5 menit tetap utuh. Konversi dilakukan sekali. | Langkah 1 |
Apa beda sampst dan timest? | sampst rata-rata per kejadian (delay); timest rata-rata terhadap waktu untuk level (jumlah di bus). | Bagian 0A |
| Kenapa data penumpang disalin ke struct? | event_schedule dan timing menimpa transfer[1] dan transfer[2]. | Langkah 4 |
| Bagaimana kamu tahu hasilnya benar? | Batas logis (maks di bus 20, min berhenti 5, min putaran 35), 110 putaran sesuai 4800 menit, Hukum Little, dan kecocokan dengan program satu berkas kami. | Bagian 6 |
| Kenapa (c) 13,6, bukan angka lain? | Bergantung definisi: penumpang dihitung keluar saat mulai turun (13,593); bila saat selesai turun hasilnya 13,853. | Bagian 6 |
| Apakah hasilnya pasti? | Tidak. Itu satu kali jalan dengan seed bawaan SIMLIB; untuk kesimpulan kuat perlu beberapa replikasi. | Bagian 7 |
| Masalah apa yang kalian temui? | Pada versi satu berkas kami: data penumpang di transfer[1..2] tertimpa SIMLIB, dan perbandingan 5 menit desimal bisa membuat bus macet; keduanya dihindari di desain repo ini. | Bagian 5 |
| Bagaimana jika rute atau laju diubah? | Cukup location_init (rute) atau konstanta di bagian atas. | Langkah 5 |