104 lines
3.7 KiB
PHP
104 lines
3.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Scheduling;
|
|
|
|
use App\Services\Scheduling\Data\SchedulingDataset;
|
|
|
|
/**
|
|
* FitnessEvaluator mengevaluasi 7 Hard Constraints (B1 - B7) pada sebuah Chromosome.
|
|
* Setiap pelanggaran aturan menambah penalti +1.
|
|
*
|
|
* Fitness = 1.0 / (1.0 + Total Penalty)
|
|
*/
|
|
class FitnessEvaluator
|
|
{
|
|
public function evaluate(Chromosome $chromosome, SchedulingDataset $dataset): void
|
|
{
|
|
$breakdown = [
|
|
'B1' => 0, // Bentrok Guru
|
|
'B2' => 0, // Bentrok Ruangan
|
|
'B3' => 0, // Ketersediaan Guru
|
|
'B4' => 0, // Bentrok Kelas
|
|
'B5' => 0, // Kualifikasi Level
|
|
'B6' => 0, // Sebaran Hari
|
|
'B7' => 0, // Konsistensi Guru
|
|
];
|
|
|
|
// Struktur pembantu untuk deteksi bentrok
|
|
// key guruSlot: "guruId_hariId_sesiId" -> count
|
|
$guruSlotMap = [];
|
|
// key ruanganSlot: "ruanganId_hariId_sesiId" -> count (khusus ruangan reguler LKP)
|
|
$ruanganSlotMap = [];
|
|
// key kelasSlot: "kelasId_hariId_sesiId" -> count
|
|
$kelasSlotMap = [];
|
|
// grouping gen per kelasId -> Gene[]
|
|
$kelasGenesMap = [];
|
|
|
|
foreach ($chromosome->genes as $gene) {
|
|
$kelas = $dataset->getKelas($gene->kelasId);
|
|
$guru = $dataset->getGuru($gene->guruId);
|
|
|
|
// B1: Bentrok Guru (1 Guru mengajar >1 kelas di (H, S) sama)
|
|
$guruSlotKey = "{$gene->guruId}_{$gene->hariId}_{$gene->sesiId}";
|
|
if (isset($guruSlotMap[$guruSlotKey])) {
|
|
$breakdown['B1']++;
|
|
} else {
|
|
$guruSlotMap[$guruSlotKey] = true;
|
|
}
|
|
|
|
// B2: Bentrok Ruangan (1 Ruangan LKP dipakai >1 kelas di (H, S) sama, khusus reguler)
|
|
if (!$kelas->isEkstrakurikuler) {
|
|
$ruanganSlotKey = "{$gene->ruanganId}_{$gene->hariId}_{$gene->sesiId}";
|
|
if (isset($ruanganSlotMap[$ruanganSlotKey])) {
|
|
$breakdown['B2']++;
|
|
} else {
|
|
$ruanganSlotMap[$ruanganSlotKey] = true;
|
|
}
|
|
}
|
|
|
|
// B3: Ketersediaan Guru (Guru diplot pada (H, S) yang TIDAK terdaftar di ketersediaannya)
|
|
if (!$guru->tersediaAt(new \App\Services\Scheduling\Data\SchedulingSlot($gene->hariId, $gene->sesiId))) {
|
|
$breakdown['B3']++;
|
|
}
|
|
|
|
// B4: Bentrok Kelas (1 Kelas diplot di (H, S) yang sama)
|
|
$kelasSlotKey = "{$gene->kelasId}_{$gene->hariId}_{$gene->sesiId}";
|
|
if (isset($kelasSlotMap[$kelasSlotKey])) {
|
|
$breakdown['B4']++;
|
|
} else {
|
|
$kelasSlotMap[$kelasSlotKey] = true;
|
|
}
|
|
|
|
// B5: Kualifikasi Level (Level Maksimal Guru < Level Program Kelas)
|
|
if ($guru->levelMaksimal < $kelas->level) {
|
|
$breakdown['B5']++;
|
|
}
|
|
|
|
// Grouping per kelas untuk B6 dan B7
|
|
$kelasGenesMap[$gene->kelasId][] = $gene;
|
|
}
|
|
|
|
// Evaluasi B6 & B7 (Khusus kelas 2x per minggu)
|
|
foreach ($kelasGenesMap as $kelasId => $genes) {
|
|
if (count($genes) == 2) {
|
|
$g1 = $genes[0];
|
|
$g2 = $genes[1];
|
|
|
|
// B6: Sebaran Hari (Hari tidak boleh sama, dan harus berjarak minimal 2 hari)
|
|
$selisihHari = abs($g1->hariId - $g2->hariId);
|
|
if ($g1->hariId == $g2->hariId || $selisihHari < 2) {
|
|
$breakdown['B6']++;
|
|
}
|
|
|
|
// B7: Konsistensi Guru (Guru pada kedua pertemuan harus sama)
|
|
if ($g1->guruId != $g2->guruId) {
|
|
$breakdown['B7']++;
|
|
}
|
|
}
|
|
}
|
|
|
|
$totalPenalty = array_sum($breakdown);
|
|
$chromosome->setEvaluationResult($totalPenalty, $breakdown);
|
|
}
|
|
}
|