IB000 Lámání čokolády Martin Milata, <256615@mail.muni.cz> 27.11.2007 1 Čokoláda s alespoň jedním sudým rozměrem Pokud je alespoň jeden rozměr čokolády sudý (s výjimkou tabulky velikosti 1x2, která už je od začátku podle pravidel nedělitelná a proto vyhrává druhý hráč), má první hráč triviální vyhrávající strategii. Hráče, který láme jako první, budu označovat jako hráče A, druhého jako hráče B. Strategie hráče A je následující: 1. Hráč A rozlomí tabulku na dvě stejně velké poloviny. Jednu si označíme jako červenou, druhou jako modrou. 2. Pokud jsou všechny kousky dále nedělitelné hra končí a vyhrává hráč A, protože lámal jako poslední. Pokud hra pokračuje, zřejmě platí, že červená i modrá část čokolády jsou rozlámány identickým způsobem. 3. Hráč B rozlomí nějaký kousek čokolády. Protože existuje i identický kousek opačné barvy, rozlomí jej stejným způsobem hráč A. 4. Pokračujeme bodem 2. 2 Tabulka s lichými rozměry Pro jednotlivé tabulky o lichých rozměrech jsem problém řešil prochazením herního stromu 1. Uzly tohoto stromu jsou jednotlivé možné stavy hry, tj. jak je v daném okamžiku nalámána čokoláda, kořen je počáteční stav hry, tj. celý kousek čokolády o daných rozměrech a listy jsou konečné stavy hry, kdy nelze zlomit další kousek a jeden z hráčů tím pádem vyhrál. Pokud je vzdálenost od kořene k listu lichá, vyhrál hráč A, pokud je sudá, vyhrál hráč B. Skutečnost zjištěná ohledně čokolády s jedním sudým rozměrem se dá zobecnit na stav čokolády, kdy jeden její díl má aspoň jeden sudý rozměr a neexistují jiné díly, které by se podle pravidel hry daly zlomit. K procházení tohoto stromu jsem použil jednoduchý rekurzivní algoritmus: Pokud neexistuje kousek, který by se dal rozlomit, hráč, který je na tahu nemůže nic dělat, proto v takové situaci vždy prohrává a výherní (prázdnou) strategii má v tomto případě hráč, který zrovna není na tahu. Pokud existuje pouze jediný rozlomitelný kousek a alespoň jeden jeho rozměr je sudý, pak hráč, který je momentálně na tahu může použít strategii popsanou v prví části tohoto dokumentu a tak si pokaždé zajistit vítězství. Tuto část algoritmu lze vynechat, slouží pouze pro jeho zrychlení. 1 Spíše se jedná o orientovaný acyklický graf, protože ke stejnému vrcholu se dá dojít více cestami a pokud jsou dva vrcholy stejné, je stejný i podstrom jejich potomků. Z důvodu jednoduchosti jsem ale pracoval se stromem, čímž jsem pravděpodobně algorimus mi těžko odhadnutelnou měrou zpomalil. 1
Pokud není splněna ani jedna z ukončovacích podmínek, pak algoritmus rekurzivně zjistí zda-li má druhý hráč vyhrávající strategii pro všechny potomky aktuálního vrcholu herního stromu. Pokud ano, aktuální hráč nemůže hru svou volbou ovlivnit a prohrál. Pokud existuje potomek, pro který nemá druhý hráč vyhrávající strategii, pak jej aktuální hráč může zvolit a tím si zajistit výhru. Důkaz správnosti algoritmu strukturální indukcí na herním stromě v podstatě kopíruje jeho definici: Báze listy stromu. Pokud neexistuje rozlomitelný kousek, pak algoritmus určí prohru hráče, který je právě na tahu, což je podle pravidel hry správně. Algoritmus tedy vrací správný výsledek pro listy stromu. Indukční krok předpokládáme, že algoritmus je správný pro všechny potomky vrcholu V a chceme dokázat, že je tím pádem správný i pro něj. Pokud algoritmus oznámí existenci výherní strategie druhého hráče pro všechny potomky vrcholu V, pak pro vrchol V určí, že aktuální hráč nemá vyhrávající strategii, což je zřejmě správné. Případ neexistence výherní strategie druhého hráče pro alespoň jednoho potomka pak znamená, že aktuální hráč vyhrávající strategii má. Algoritmus je tedy korektní pro všechny vrcholy herního stromu, včetně kořene. 3 Vypočítané hodnoty Vzhledem k exponenciální složitosti algoritmu a jeho pravděpodobně nepříliš efektivní implementaci se mi podařilo určit hráče s výherní strategií pouze u velmi malých hodnot m a n. A znamená, že vždy vyhrávající strategii má hráč, který začíná lámat, B znamená, že ji má druhý hráč a,,- znamená, že jsem z časových důvodů nebyl schopen hráče určit. m/n 1 3 5 7 9 1 B 3 B B 5 A B B 7 B B B B 9 A B B - - 11 B B - - - 13 A B - - - 15 A A - - - 17 B B - - - 19 A - - - - 21 A - - - - 23 B - - - - Pro m=1 má hráč A dále vyhravající strategii pro tato n: 25, 29, 33, 35; hrac B pak pro 27, 31, 37. 4 Zdrojový kód Algoritmus je implementován ve funkcionálním jazyce Haskell. Následuje kompletní zdrojový kód. 2
module Main where import Data. List import System. Environment type Piece = ( Int, Int, Int ) mensi rozmer, v e t s i rozmer pocet, pocet type Cut = [ Piece ] n e k o l i k kousku cokolady herni s t a v h l a v n i funkce z k o n t r o l u j e argumenty predane na prikazovem radku, prevede j e na c i s l a, preda j e f u n k c i whowins, k t e r a e v e n t u a l n e v r a t i hrace s vyherni s t r a t e g i i, k t e r e h o funkce main v y p i s e main : : IO ( ) main = do args < getargs i f length args /= 2 then error Spatny p o c e t argumentu else do l e t m = head args n = head $ t a i l args putstr ( Rozmery ++ m ++ x ++ n ++ : ) i f whowins ( read m) ( read n ) then putstrln Prvni hrac ma viteznou s t r a t e g i i else putstrln Druhy hrac ma viteznou s t r a t e g i i wrapper o k o lo playerwins, argumenty j s o u rozmery cokolady, v r a c i t r u e pokud ma v i t e z n o u s t r a t e g i i prvni hrac, j i n a k f a l s e whowins : : Int > Int > Bool whowins m n = playerwins $ normalize [ (m, n, 1 ) ] v r a t i true, pokud ma hrac, k t e r y j e v t e t o p o z i c i na tahu pro danou herni p o z i c i vyherni s t r a t e g i i pokud se cokolada j i z neda rozlomit, hrac vzdy prohrava pokud ma cokolada j e d i n y podle p r a v i d e l r o z l o m i t e l n y d i l e k a ten ma alespon jeden sudy rozmer, hrac muze vzdy vyhrat v o s t a t n i c h pripadech j e r e k u r z i v n e zkontrolovano, j e s t l i alespon jedno z moznych rozlomeni ma pro t o h o t o hrace vyherni s t r a t e g i i ( t zn. zda l i vsechna rozlomeni nevyhrava druhy hrac ) playerwins : : Cut > Bool playerwins [ ] = False playerwins c s i n g l e E v e n S i d e c = True otherwise = not ( a l l playerwins ( cuts c ) ) 3
v r a t i true, pokud ma cokolada j e d i n y r o z l o m i t e l n y d i l e k a ten ma alespon jeden rozmer sudy s i n g l e E v e n S i d e : : Cut > Bool s i n g l e E v e n S i d e [ (m, n, c ) ] = c == 1 && ( even m even n ) s i n g l e E v e n S i d e c = False v r a t i seznam vsech moznych rozlomeni cokolady, k t e r e j e mozne p odle p r a v i d e l u d e l a t jednim zlomenim jednoho kousku cokolady predanych jako argument cuts : : Cut > [ Cut ] cuts c = nub $ sortby cmpcuts $ map normalize $ concat $ zipwith f [ 0.. ( length c ) 1] ( repeat c ) where f n px = map (\ x > x ++ take n px ++ drop ( n+1) px ) ( b r e a k p i e c e ( px!! n ) ) porovnavaci funkce, k t e r a urcuje v jakem poradi ma funkce c u t s v r a c e t mozna rozlomeni cmpcuts : : Cut > Cut > Ordering cmpcuts xs ys s i n g l e E v e n S i d e xs = LT s i n g l e E v e n S i d e ys = GT otherwise = compare ( area xs ) ( area ys ) where area = foldr ( \ (m, n, c ) a > a+m n c ) 0 funkce v r a c e j i c i vsechna mozna rozlomeni jednoho kousku cokolady j s o u vytvoreny vsechny mozne v e r t i k a l n i i h o r i z o n t a l n i rozlomeni, prevedeny do normalniho tvaru, odstraneny d u p l i c i t y a odstraneny rozlomeni s kouskem 1x1, p r o t o z e takove neni podle p r a v i d e l l e g a l n i b r e a k p i e c e : : Piece > [ Cut ] b r e a k p i e c e p = f i l t e r not1x1 $ nub $ map normalize $ ( breakph p ) ++ ( breakpv p ) where not1x1 ( (m, n, c ) : xs ) = not (m == n && n == 1) not1x1 [ ] = True funkce v r a c e j i c i vsechna mozna rozlomeni kousku h o r i z o n t a l n e breakph : : Piece > [ Cut ] breakph (m, n, c ) = zipwith f [ 1.. m div 2 ] ( repeat (m, n, c ) ) where f x (m, n, c ) = [ ( x, n, 1 ), (m x, n, 1 ) ] ++ i f c > 1 then [ (m, n, c 1)] else [ ] 4
funkce v r a c e j i c i vsechna mozna rozlomeni kousku v e r t i k a l n e breakpv : : Piece > [ Cut ] breakpv (m, n, c ) = zipwith f [ 1.. n div 2 ] ( repeat (m, n, c ) ) where f x (m, n, c ) = [ (m, x, 1 ), (m, n x, 1 ) ] ++ i f c > 1 then [ (m, n, c 1)] else [ ] funkce p r e v a d e j i c i rozlamanou cokoladu do normalniho tvaru, tzn. o t o c i vsechny kousky tak, aby j e j i c h prvni rozmer b y l mensi nebo roven druhemu, s e r a d i j e podle v e l i k o s t i, s l o u c i kousky s t e j n e v e l i k o s t i a o d s t r a n i kousky v e l i k o s t i 1x2 a 1x3, p r o t o z e t y uz se n e d a j i d a l e r o z l o m i t a tim padem j s o u pro nas nezajimave normalize : : Cut > Cut normalize = k i l l 1 2 1 3. unpieces. s o r t P i e c e s. normpieces o t o c e n i kousku normpieces : : Cut > Cut normpieces [ ] = [ ] normpieces ( (m, n, c ) : ps ) = normal : ( normpieces ps ) where normal = i f m > n then (n,m, c ) else (m, n, c ) s e r a z e n i kousku s o r t P i e c e s : : Cut > Cut s o r t P i e c e s = sort s l o u c e n i kousku s t e j n e v e l i k o s t i unpieces : : Cut > Cut unpieces [ ] = [ ] unpieces ( p : [ ] ) = p : [ ] unpieces ( (m, n, c ) : (m, n, c ) : ps ) = i f m == m && n == n then unpieces ( (m, n, c+c ) : ps ) else (m, n, c ) : ( unpieces ( (m, n, c ) : ps ) ) o d s t r a n e n i kousku v e l i k o s t 1x2 a 1x3 k i l l 1 2 1 3 : : Cut > Cut k i l l 1 2 1 3 ( (m, n, c ) : xs ) m == 1 && n == 2 = k i l l 1 2 1 3 xs m == 1 && n == 3 = xs k i l l 1 2 1 3 xs = xs 5