319 lines
8.2 KiB
Markdown
319 lines
8.2 KiB
Markdown
# 🔧 Kapadokya Kuralları - Kritik Düzeltme
|
||
|
||
## ❌ Tespit Edilen Sorun
|
||
|
||
Kapadokya kuralları tanımlı olmasına rağmen, **SMART RESTAURANT** bölümünde kural kontrolü yapılmıyordu. Bu, LIMITED kuralının (günde sadece 1 restaurant/cafe) bypass edilmesine neden oluyordu.
|
||
|
||
### Sorunlu Kod Akışı
|
||
|
||
```
|
||
GÜN 1:
|
||
1. FLEXIBLE PLACES döngüsü
|
||
→ Cafe eklendi ✅ (isValidForDay kontrolü var)
|
||
|
||
2. SMART RESTAURANT bölümü
|
||
→ Restaurant eklendi ❌ (isValidForDay kontrolü YOK!)
|
||
→ LIMITED kuralı bypass edildi!
|
||
|
||
SONUÇ: Aynı günde hem cafe hem restaurant var ❌
|
||
```
|
||
|
||
## ✅ Uygulanan Düzeltme
|
||
|
||
### Değişiklik: `/src/db/api.ts` (Satır 1042-1074)
|
||
|
||
#### ÖNCE (Hatalı)
|
||
```typescript
|
||
/* ---- SMART RESTAURANT (1 per day, near centroid) ---------------- */
|
||
const hasRestaurant = dayPlaces.some(isRestaurant);
|
||
if (!hasRestaurant && dayPlaces.length > 0) {
|
||
const center = getCentroid(dayPlaces);
|
||
|
||
if (center) {
|
||
const restaurants = scoredPlaces
|
||
.filter(
|
||
p =>
|
||
isRestaurant(p) &&
|
||
!usedPlaceIds.has(p.id) &&
|
||
p.latitude &&
|
||
p.longitude
|
||
)
|
||
.map(r => ({
|
||
...r,
|
||
d: distance(center, {
|
||
lat: r.latitude,
|
||
lng: r.longitude,
|
||
}),
|
||
}))
|
||
.filter(r => r.d < 1500)
|
||
.sort((a, b) => a.d - b.d);
|
||
|
||
if (restaurants.length > 0) {
|
||
dayPlaces.splice(1, 0, restaurants[0]); // ❌ Doğrudan ekleniyor
|
||
usedPlaceIds.add(restaurants[0].id);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### SONRA (Düzeltilmiş)
|
||
```typescript
|
||
/* ---- SMART RESTAURANT (1 per day, near centroid) ---------------- */
|
||
const hasRestaurant = dayPlaces.some(isRestaurant);
|
||
if (!hasRestaurant && dayPlaces.length > 0) {
|
||
const center = getCentroid(dayPlaces);
|
||
|
||
if (center) {
|
||
const restaurants = scoredPlaces
|
||
.filter(
|
||
p =>
|
||
isRestaurant(p) &&
|
||
!usedPlaceIds.has(p.id) &&
|
||
p.latitude &&
|
||
p.longitude
|
||
)
|
||
.map(r => ({
|
||
...r,
|
||
d: distance(center, {
|
||
lat: r.latitude,
|
||
lng: r.longitude,
|
||
}),
|
||
}))
|
||
.filter(r => r.d < 1500)
|
||
.sort((a, b) => a.d - b.d);
|
||
|
||
if (restaurants.length > 0) {
|
||
const restaurant = restaurants[0];
|
||
|
||
// ✨ KURAL KONTROLÜ: Type-based validation (LIMITED rule)
|
||
if (isValidForDay(restaurant, dayPlaces, usedPlaceIds, { balloonAdded })) {
|
||
dayPlaces.splice(1, 0, restaurant); // ✅ Kural kontrolünden sonra ekleniyor
|
||
usedPlaceIds.add(restaurant.id);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 🎯 Düzeltmenin Etkisi
|
||
|
||
### Senaryo: Cafe + Restaurant Aynı Günde
|
||
|
||
#### ❌ ÖNCE (Hatalı Davranış)
|
||
```
|
||
GÜN 1:
|
||
1. FLEXIBLE PLACES
|
||
→ Göreme Açık Hava Müzesi (museum) ✅
|
||
→ Cafe Safak (cafe) ✅
|
||
→ Paşabağ Vadisi (valley) ✅
|
||
|
||
2. SMART RESTAURANT
|
||
→ hasRestaurant = false (çünkü cafe != restaurant)
|
||
→ Seten Restaurant eklendi ❌ (KURAL İHLALİ!)
|
||
|
||
SONUÇ: Aynı günde hem cafe hem restaurant ❌
|
||
```
|
||
|
||
#### ✅ SONRA (Doğru Davranış)
|
||
```
|
||
GÜN 1:
|
||
1. FLEXIBLE PLACES
|
||
→ Göreme Açık Hava Müzesi (museum) ✅
|
||
→ Cafe Safak (cafe) ✅
|
||
→ Paşabağ Vadisi (valley) ✅
|
||
|
||
2. SMART RESTAURANT
|
||
→ hasRestaurant = false
|
||
→ Seten Restaurant adayı bulundu
|
||
→ isValidForDay() kontrolü:
|
||
- dayPlaces'te LIMITED tip var mı? → EVET (cafe)
|
||
- return false
|
||
→ Restaurant REDDEDİLDİ ✅
|
||
|
||
SONUÇ: Günde sadece 1 LIMITED tip (cafe) ✅
|
||
```
|
||
|
||
## 📊 Tüm Ekleme Noktaları
|
||
|
||
### Yer Ekleme Noktaları ve Kontrolleri
|
||
|
||
| Satır | Bölüm | Kontrol | Durum |
|
||
|-------|-------|---------|-------|
|
||
| 1021 | BALLOON | `shouldAddBalloon()` | ✅ Var |
|
||
| 1038 | FLEXIBLE PLACES | `isValidForDay()` | ✅ Var |
|
||
| 1068 | SMART RESTAURANT | `isValidForDay()` | ✅ **YENİ!** |
|
||
| 1085 | MIN FILL | `isValidForDay()` | ✅ Var |
|
||
|
||
### Kural Kontrolü Akışı
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ YER EKLEME AKIŞI │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
|
||
1️⃣ BALLOON
|
||
shouldAddBalloon() → ✅ Özel kural kontrolü
|
||
|
||
2️⃣ FLEXIBLE PLACES
|
||
FOR LOOP
|
||
→ isValidForDay() → ✅ Genel kural kontrolü
|
||
|
||
3️⃣ SMART RESTAURANT
|
||
IF !hasRestaurant
|
||
→ isValidForDay() → ✅ **YENİ!** Genel kural kontrolü
|
||
|
||
4️⃣ MIN FILL
|
||
FOR LOOP
|
||
→ isValidForDay() → ✅ Genel kural kontrolü
|
||
```
|
||
|
||
## 🧪 Test Senaryoları
|
||
|
||
### Test 1: LIMITED Kuralı (Restaurant + Cafe)
|
||
|
||
**Senaryo:** 2 günlük seyahat, 1 cafe + 1 restaurant seçili
|
||
|
||
**Beklenen Davranış:**
|
||
```
|
||
GÜN 1:
|
||
✅ Göreme Müzesi (museum)
|
||
✅ Cafe Safak (cafe) - FLEXIBLE PLACES'ten
|
||
✅ Paşabağ Vadisi (valley)
|
||
❌ Restaurant - SMART RESTAURANT reddetti (LIMITED kuralı)
|
||
|
||
GÜN 2:
|
||
✅ Zelve Müzesi (museum)
|
||
✅ Seten Restaurant (restaurant) - SMART RESTAURANT'tan
|
||
✅ Devrent Vadisi (valley)
|
||
```
|
||
|
||
### Test 2: SMART RESTAURANT Önceliği
|
||
|
||
**Senaryo:** 2 günlük seyahat, restaurant yok, cafe yok
|
||
|
||
**Beklenen Davranış:**
|
||
```
|
||
GÜN 1:
|
||
✅ Göreme Müzesi (museum)
|
||
✅ Seten Restaurant (restaurant) - SMART RESTAURANT'tan
|
||
✅ Paşabağ Vadisi (valley)
|
||
|
||
GÜN 2:
|
||
✅ Zelve Müzesi (museum)
|
||
✅ Dibek Restaurant (restaurant) - SMART RESTAURANT'tan
|
||
✅ Devrent Vadisi (valley)
|
||
```
|
||
|
||
### Test 3: FLEXIBLE PLACES'te Cafe Varsa
|
||
|
||
**Senaryo:** FLEXIBLE PLACES döngüsünde cafe eklendi
|
||
|
||
**Beklenen Davranış:**
|
||
```
|
||
GÜN 1:
|
||
1. FLEXIBLE PLACES
|
||
✅ Göreme Müzesi (museum)
|
||
✅ Cafe Safak (cafe)
|
||
✅ Paşabağ Vadisi (valley)
|
||
|
||
2. SMART RESTAURANT
|
||
hasRestaurant = true (cafe var)
|
||
→ Bölüm atlandı ✅
|
||
```
|
||
|
||
## 🔍 Kritik Fark
|
||
|
||
### hasRestaurant vs isValidForDay
|
||
|
||
#### `hasRestaurant` Kontrolü
|
||
```typescript
|
||
const hasRestaurant = dayPlaces.some(isRestaurant);
|
||
```
|
||
- **Amaç:** Restaurant/cafe var mı kontrol et
|
||
- **Sorun:** Sadece restaurant/cafe tiplerini kontrol eder
|
||
- **Eksiklik:** LIMITED kategorisini kontrol etmez
|
||
|
||
#### `isValidForDay()` Kontrolü
|
||
```typescript
|
||
if (category === 'LIMITED') {
|
||
const hasLimitedType = dayPlaces.some((p) => {
|
||
const pCategory = getPlaceCategory(p.type || 'default');
|
||
return pCategory === 'LIMITED';
|
||
});
|
||
return !hasLimitedType;
|
||
}
|
||
```
|
||
- **Amaç:** LIMITED kategorisindeki TÜM tipleri kontrol et
|
||
- **Avantaj:** Kategori bazlı kontrol
|
||
- **Sonuç:** Restaurant ve cafe'yi aynı kategoride ele alır
|
||
|
||
## 📈 Düzeltme Öncesi vs Sonrası
|
||
|
||
### Önce (Hatalı)
|
||
```
|
||
FLEXIBLE PLACES:
|
||
✅ isValidForDay() kontrolü var
|
||
✅ LIMITED kuralı uygulanıyor
|
||
|
||
SMART RESTAURANT:
|
||
❌ isValidForDay() kontrolü YOK
|
||
❌ LIMITED kuralı bypass ediliyor
|
||
|
||
MIN FILL:
|
||
✅ isValidForDay() kontrolü var
|
||
✅ LIMITED kuralı uygulanıyor
|
||
|
||
SONUÇ: Kurallar kısmen çalışıyor ❌
|
||
```
|
||
|
||
### Sonra (Düzeltilmiş)
|
||
```
|
||
FLEXIBLE PLACES:
|
||
✅ isValidForDay() kontrolü var
|
||
✅ LIMITED kuralı uygulanıyor
|
||
|
||
SMART RESTAURANT:
|
||
✅ isValidForDay() kontrolü var
|
||
✅ LIMITED kuralı uygulanıyor
|
||
|
||
MIN FILL:
|
||
✅ isValidForDay() kontrolü var
|
||
✅ LIMITED kuralı uygulanıyor
|
||
|
||
SONUÇ: Kurallar tam olarak çalışıyor ✅
|
||
```
|
||
|
||
## 🎓 Öğrenilen Dersler
|
||
|
||
### 1. Tüm Ekleme Noktalarını Kontrol Et
|
||
- Bir yerin timeline'a eklendiği TÜM noktaları bul
|
||
- Her noktada aynı kural kontrolünü uygula
|
||
- Hiçbir bypass noktası bırakma
|
||
|
||
### 2. Helper Fonksiyonlar Yeterli Değil
|
||
- `hasRestaurant` gibi helper'lar sadece tip kontrolü yapar
|
||
- Kategori bazlı kurallar için `isValidForDay()` gerekli
|
||
- Her ekleme noktasında `isValidForDay()` çağır
|
||
|
||
### 3. Test Senaryoları Önemli
|
||
- Farklı kod yollarını test et
|
||
- Edge case'leri kontrol et
|
||
- Gerçek kullanım senaryolarını simüle et
|
||
|
||
## ✅ Sonuç
|
||
|
||
**Sorun:** SMART RESTAURANT bölümü LIMITED kuralını bypass ediyordu
|
||
|
||
**Çözüm:** `isValidForDay()` kontrolü eklendi
|
||
|
||
**Etki:** Artık TÜM ekleme noktalarında kurallar enforce ediliyor
|
||
|
||
**Durum:** ✅ Düzeltildi ve test edildi
|
||
|
||
---
|
||
|
||
**Tarih:** 2025
|
||
**Durum:** ✅ Tamamlandı
|
||
**Değişiklik:** 1 dosya, 1 bölüm
|
||
**Satır:** 1042-1074
|