Szinte minden stratégiai játék alapja a gazdaság. A klasszikus Dune II-féle modell roppant egyszerű: bemegy a spice, kijön a tank.
Bár ez így nagyon primitívnek tűnik, a "valóság" vagyis inkább csak egyes közgazdasági modellek sem sokkal bonyolultabbak. Vannak erőforrások (a fenti példában a spice és a tank) meg gyáraink (tankgyár), és egy nagy mátrix, ami megadja, hogy az egyes gyárak az egyes erőforrásokból mennyit használnak fel vagy állítanak elő egységnyi idő alatt.
TANKGYÁR | |
SPICE | -5 |
TANK | +1 |
Ennek az egyszerűségnek több előnye is van. Egyrészt könnyű felfogni. Márpedig ha nem kifejezetten gazdaságszimulátorral játszik valaki, nem biztos, hogy el akar merülni akár a számvitel, akár a derivatívák rejtelmeiben.
Másrészt könnyű megvalósítani. A játék PHP+MySQL+AJAX-alapú, maga a gazdaság MySQL-ben készül. Ez elsőre talán furcsának tűnik, de ha azt nézzük, hogy a játékosok nagy számban egymással párhuzamosan kérdezik le és módosítják (építkezéssel, háborúval) a gazdaság állapotát, akkor máris jól jön egy rendes adatbáziskezelő, ami ezeket gond nélkül elintézi. Szemben egy C-ben barkácsolt modullal, ahol ezeket mind le kell programozni a gazdasági magon kívül.
Tehát
A fenti modellből kiindulva három lényeges táblánk van:
- bolygo_eroforras (bolygo_id, eroforras_id, db): megadja, hogy melyik bolygón melyik erőforrásból hány darab van
- bolygo_gyar (bolygo_id, gyar_id, db): megadja, hogy melyik bolygón melyik gyár(típus)ból hány darab van (vagyis két tankgyár egy bolygón az egy bejegyzés, ahol db=2)
- gyar_eroforras (gyar_id, eroforras_id, io): megadja, hogy melyik gyár(típus) melyik erőforrásból mennyit állít elő vagy használ fel (input/output)
Ez alapján roppant egyszerűnek tűnik összedobni egy lekérdezést, ami kiszámolja, hogy melyik bolygón melyik erőforrásból mekkora növekedés vagy csökkenés megy végbe. Csakhogy van egy bökkenő. Ha hiány van egy erőforrásból, mondjuk nincs 5 spice, akkor a tankgyár nem tud termelni, hiszen negatívba egyik erőforrásból sem mehetünk (per pill nincsenek bankok). Oké, akkor várjuk meg, amíg összejön az 5 spice, addig álljon le a gyár. De mi van, ha több gyár is használ egy erőforrást, amiből részleges hiány van? Vagyis van 5 spice-unk, de a tankgyáron kívül egy másik üzem is felhasználna spice-ot. Melyik termeljen? Várjuk meg, amíg mindenki számára elég input összegyűlik, és akkor egyszerre termelhet az összes egy kört?
Először pontosan így volt leprogramozva a dolog. Aztán Arthur bá elküldte a tutit. Ha egy erőforrást több gyár is használ inputnak, akkor osszuk fel köztük olyan arányban, amilyen arányban igénylik. És mindegyik gyár olyan teljesítménnyel üzemeljen, amennyi inputja van a teljes igényéhez képest. Vagyis ha van 4 tankgyár és 2 barakk, amik 3 spice-ból csinálnak egy katonát, akkor a spice 6/26-od része megy a barakkoknak, 20/26-ad része a tankgyáraknak. Így 13 spice esetén 3 spice-ot kapnak a barakkok (és termelnek 1 katonát), 10-et pedig a tankgyárak (és termelnek 2 tankot).
És hogy néz ki ez a gyakorlatban?
- Előállítunk egy bolygo_gyar_eroforras táblát, ami bolygónként megadja, hogy melyik gyárból hány van, ezek az egyes erőforrásokból mennyit használnak fel, és milyen arányban osztoznak rajta:
select bgy.bolygo_id,bgy.gyar_id,gye.eroforras_id,bgy.aktiv_db,gye.io,if(gye.io>=0,0,floor(100*bgy.aktiv_db*gye.io/sumiotabla.sumio)) as reszarany from ( select bgy.bolygo_id,gye.eroforras_id,sum(bgy.aktiv_db*if(gye.io>=0,0,gye.io)) as sumio from bolygo_gyar bgy,gyar_eroforras gye where bgy.gyar_id=gye.gyar_id group by bgy.bolygo_id,gye.eroforras_id ) sumiotabla,bolygo_gyar bgy,gyar_eroforras gye where bgy.gyar_id=gye.gyar_id and bgy.bolygo_id=sumiotabla.bolygo_id and gye.eroforras_id=sumiotabla.eroforras_id
- A következő a bgy_eff tábla, ami bolygónként megadja a gyárak effektív számát, vagyis hogy hány működik közülük az adott körben (a fenti példában 4 tankgyárból 2 működik):
select bgye.bolygo_id,bgye.gyar_id,min(if(bgye.io>=0,bgye.aktiv_db,if(bgye.aktiv_db*bgye.io*100+be.db*bgye.reszarany>=0,bgye.aktiv_db,floor(-be.db*bgye.reszarany/100/bgye.io)))) as effektiv_db from bolygo_gyar_eroforras bgye,bolygo_eroforras be where bgye.bolygo_id=be.bolygo_id and bgye.eroforras_id=be.eroforras_id group by bgye.bolygo_id,bgye.gyar_id
- Ebből már meghatározható, hogy melyik bolygón melyik erőforrás mennyivel változik (deltatabla):
select be.bolygo_id,be.eroforras_id,sum(gye.io*bgy_eff.effektiv_db) as delta from bgy_eff,bolygo_eroforras be,gyar_eroforras gye where be.eroforras_id=gye.eroforras_id and be.bolygo_id=bgy_eff.bolygo_id and bgy_eff.gyar_id=gye.gyar_id group by be.bolygo_id,be.eroforras_id
- És végül update-eljük a bolygo_eroforras táblát:
update bolygo_eroforras be,deltatabla set be.db=be.db+deltatabla.delta where be.bolygo_id=deltatabla.bolygo_id and be.eroforras_id=deltatabla.eroforras_id
Megjegyzés: a bolygo_gyar tábla aktiv_db mezője azt adja meg, hogy hány gyárat működtetünk (ennek akkor lehet szerepe, ha valamiért vissza akarjuk fogni a gazdaságunkat gyárrombolás nélkül). A bolygo_gyar_eroforras táblát ténylegesen is előállítjuk, mert ez csak akkor változik, ha épül vagy lerombolódik egy gyár. A többi hármat viszont össze lehet pakolni egyetlen összetett lekérdezésbe.
És hogy mennyire gyors?
A legkorábbi változat egy E6300 Core 2 Duo-n, 5 erőforrással, 5 gyárral és 10ezer bolygóval 3,4s alatt futott le. MEMORY táblákkal (ezek állandóan a memóriában vannak, így nem kell szarakodni az I/O-műveletekkel, viszont ha kifagy a gép, akkor elveszik a tartalma) ugyanez csak 0,8s. A fenti javított verzió pedig 0,5s.
Mivel a játék várhatóan hónapokig fog tartani, nem kell másodpercenként termelnie a gyáraknak, hiszen úgysem ülnek ott a játékosok folyamatosan, hogy reagáljanak. Ezt majd a tesztfázisban lehet rendesen belőni, de nagyságrendileg az 5-10 percenkénti frissítés jónak tűnik. Ez alapján pedig a fél másodperc futási idő teljesen elfogadható.
Utolsó kommentek