Nederlandstalige pagina.
Hier kun je de spelregels van Box en de technische regels vinden.
Omdat volgend jaar de regels mogelijk zijn gearchiveerd, heb ik hier een paar PDF files bewaard. Bij voorkeur bekijk je de officiele regels vanwege mogelijke updates (na 15 september 2024). PDF Spelregels PDF Technische regels
Meeste punten scoren door op hoekpunten van vierkanten gelijke kleuren te zetten, specifiek van de eigen geheime kleur.
Wat zouden we moeten weten over het spel, dat niet uit de regels te halen valt?
Hoe lang duurt het spel gemiddeld? Of hoeveel tegels/stenen worden er gemiddeld per spel gelegd? Is dit aantal mogelijk sterk afhankelijk van gevolgde strategie?
Is het altijd nodig om de score voor alle kleuren bij te houden? Betekent een hoge score voor een bepaalde kleur die niet de onze is ook dat deze de kleur van de tegenstander vertegenwoordigt? Wanneer in het spel tekent een score verdeling zich af?
Als je op een gegeven ogenblik ergens geen blok horizontaal of vertikaal mag plaatsen, dan mag dat in de volgende beurten op die plek ook niet.
Bijhouden van het aantal gespeelde zetten. Bijhouden totaal gevulde velden.
Hoe kun je zo snel mogelijk de toegestane zetten bepalen? Is dit door afbakenen van de mogelijkheden of toch door brute force en aftesten?
Hoe kun je zo effektief mogelijk de score van elke kleur bijhouden?
Hoe moet je zelf een score voorbereiden en afronden?
Na het vaststellen van een score kan er bij het betreffende hoekpunt nog een grotere of meerdere grotere vierkanten die een score geven zijn?
Wat zijn mogelijkheden om het spel strategisch te spelen? Dit zijn evaluatiebeginselen die kunnen verschillen.
Het bord snel vullen? Is dit mogelijk en kan het de kansen vergroten?
Winnen betekent zelf ergens een score maken.
Gaan voor veel kleine scores.
Gaan voor een enkele grote score.
Voorkeur voor horizontaal leggen? Voorkeur voor vertikaal leggen?
Tegenhouden van andere scores
Als het mogelijk is om alle mogelijke vervolg zetten tot een aantal van 0 terug te brengen, dan moet je dat doen als de score in je voordeel uitpakt.
Is aanleggen gunstiger of is overlappen gunstiger?
Hier gaan we een ontwerp vormgeven
Veld
Speelbord met eerste tegel
Het veld is 20 breed bij 16 hoog. Dat zijn dus 320 velden
LET spelbord binary! 320
Op sommige plaatsen van het bord kun je geen tegel neerleggen, want dan gaat die over de rand.
Tegels vertikaal aan de eerste aanleggen
Tegels horizontaal aan de eerste aanleggen
We merken gelijk op dat voor iedere tegel er ook velden geblokkeerd worden waarop in bepaalde richting meteen duidelijk is waar geen tegel gelegd mag worden in verband met overlap.
Voor een vertikaal geplaatste tegel zien de toegestane velden er zo uit:
Tegels vertikaal aan een vertikale tegel aanleggen
Tegels horizontaal aan een vertikale tegel aanleggen
De allereerste tegel wordt altijd horizontaal op Hh gelegd door het competitiesysteem.
LET [horizontal-block vertical-block] binary! 320
change/repeat horizontal-block 1 320
change/repeat vertical-block 0 320
the-horizontal: ""
space= " "
for index to whole! 15 [
plek: index - 1 * 20 + 1
change/repeat at horizontal-block plek 0 15
]
for index 320 [
the-horizontal: join/with join/with the-horizontal to string! pick horizontal-block index space
unless modulo index 20 [
the-horizontal: join/with the-horizontal new-line
]
]
write/line the-horizontal
for index 10 [
poke vertical-block 20 * index 1
]
change/repeat at vertical-block 220 1 101
the-vertical: ""
for index 320 [
the-vertical: join/with join/with the-vertical to string! pick vertical-block index space
unless modulo index 20 [
the-vertical: join/with the-vertical new-line
]
]
write/line the-vertical
Snel Lettercombinatie naar veldnummer omzetten. Instructie voor de zet van de opponent moet efficient vertaald worden. "Id123456h"
instruction: "Id123456h"
horizontal-position: first-of instruction
advance instruction
vertical-position: first-of instruction
advance instruction
tile-1: to byte! copy cut instruction 1
advance instruction
tile-2: to byte! copy cut instruction 1
advance instruction
tile-3: to byte! copy cut instruction 1
advance instruction
tile-4: to byte! copy cut instruction 1
advance instruction
tile-5: to byte! copy cut instruction 1
advance instruction
tile-6: to byte! copy cut instruction 1
advance instruction
horizontal?: find/here instruction "h" [TRUE][FALSE]
start-index: horizontal-position - 65 * 20 + (vertical-position - 96)
Nagaan of het allemaal goed gaat
space= " "
write {De instructie was:
Plaats op index } write start-index write " een tegel " write either horizontal? ["horizontaal"]["verticaal"]
write " met achtereenvolgens de waarden: "
write tile-1 write space
write tile-2 write space
write tile-3 write space
write tile-4 write space
write tile-5 write space
write tile-6 write/line space
Hierna moeten we de zet op het bord verwerken. Tegel op het bord zetten(1) toegevoegde mogelijke velden bijwerken onmogelijke velden bijwerken op horizontaal en vertikaal.
Gelet op de lengte van de code is het toch wellicht handig om een tegel als array te declareren.
let [plaats-tegel mijn-tegel] binary! 6
change/repeat plaats-tegel 0 6
change/repeat mijn-tegel 0 6
instruction: "Id123456h"
horizontal-position: first-of instruction
advance instruction
vertical-position: first-of instruction
advance instruction
whole16! index ; maak groot genoeg aan
for index 6 [
poke plaats-tegel index to byte! copy cut instruction 1
advance instruction
]
horizontal?: find/here instruction "h" [TRUE][FALSE]
start-index: horizontal-position - 65 * 20 + (vertical-position - 96)
Als we de tegel op het bord leggen moeten we daar code voor hebbben
Let spelbord binary! 320
let plaats-tegel binary! 6
byte! tel
whole16! index
change/repeat spelbord 0 320
change/repeat plaats-tegel 0 6
for tel 6 [ poke plaats-tegel tel tel]
start-index: 88
;either horizontal? [
for index 6 [
poke spelbord start-index + index - 1 pick plaats-tegel index
poke spelbord start-index + 26 - index pick plaats-tegel index
]
start-index: 125
;][ ; Vertikaal
for index 6 [
poke spelbord index - 1 * 20 + start-index pick plaats-tegel index
poke spelbord index - 1 * 20 + start-index + 1 pick plaats-tegel (7 - index)
]
;]
; en even wat testcode
speelbord: ""
for index 320 [
speelbord: join/with join/with speelbord to string! pick spelbord index " "
unless modulo index 20 [
speelbord: join/with speelbord new-line
]
]
write/line speelbord
Testresultaat
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1 2 3 4 5 6 0 0 0 0 0 0 0
0 0 0 0 0 0 0 6 5 4 3 2 1 0 0 0 0 0 0 0
0 0 0 0 1 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 2 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 3 4 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 4 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 5 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 6 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Het werkt.
Als we onze zet gaan bepalen moeten we eerst uitgaan van de plaatsen waar we nu een tegel mogen neerzetten. We hebben weliswaar bijgehouden waar dit mag maar dit hebben we gedaan voor de bestaande tegels en voor de toegevoegde tegel. Het is mogelijk dat we tegels willen zetten op een plaats die door de combinatie van alle gelegde tegels nu ook niet langer is toegestaan. Er is dan een nieuwe overlap van meer dan 4 tegelpunten ontstaan. We moeten dus altijd even checken. Als de plek niet is toegestaan dan werken we dat bij in de administratie dan hoeven we dat geen tweede of volgende keer meer te doen.
Eigen scores kun je gelijk met alle andere scores bepalen. We kennen de geheime kleur van de opponent uiteraard toch ook niet. Gelijk met een beetje testcode
let scores binary! 6
whole16! tel
for tel 6 [poke scores tel 0]
let scorebord binary! 320
random/seed now
for teller 320 [
random-byte: random 255
while random-byte > 252 [
random-byte: random 255
]
random-0-6: modulo random-byte 7
poke scorebord teller random-0-6
]
speelbord: ""
for index 320 [
speelbord: join/with join/with speelbord to string! pick scorebord index " "
if 0 = modulo index 20 [
speelbord: join/with speelbord new-line
]
]
write/line speelbord
max-score: 16
for rij 15 [
for kolom 19 [
if 0 < pick scorebord (rij - 1 * 20 + kolom) [
vind: pick scorebord (rij - 1 * 20 + kolom)
for k2 [kolom + 1 max-score] [
if vind = pick scorebord (rij - 1 * 20 + k2) [
if all [vind = pick scorebord (rij - 1 + k2 - kolom * 20 + (kolom))
vind = pick scorebord (rij - 1 + k2 - kolom * 20 + (k2))] [
write "score " write k2 - kolom write " voor " write vind
write " op rij " write rij write " en kolom " write/line kolom
poke scores vind k2 - kolom + pick scores vind
]
]
]
]
]
decrement max-score
]
write/line "Scores:"
for tel 6 [
write pick scores tel write " "
]
write/line " "
Dit is natuurlijk link want met enkele grote scores zou je hier in theorie zo over de grens van 256 heen kunnen gaan, maar het zou bijvoorbeeld 16 maal een score van 16 moeten zijn. Aangezien 15 de grootste score is en je geen 16 keer de maximale score kunt halen, is de verwachting dat we voorlopig hiermee weg kunnen komen.
Tegeljkertijd blijkt uit de testcode dat het weinig gebeurd dat gelijke getallen op 4 hoekpunten voorkomen. Ongeveer 1 op de 3 pogingen slaagt daarin en dan nog maar met een enkele score. Het algoritme lijkt wel snel genoeg.
eigen-kleur: 1
Mogelijkheden voor invoer:
Start
Quit
1 t/m 6 voor eigen geheime kleur
123456 voor toegewezen tegelkleuren
Uitvoer:
De eigen zet in formaat "Aah".
Tot einde spel lees invoer van stdin en verwerk actie
QUIT
GEHEIME KLEUR
START
ZET
RIJ VAN ZES KLEUREN
Programmacode in opbouw, sommige namen van variabelen krijgen andere namen, omdat veel code even snel in elkaar gezet is om te kijken of het werkt als bedoeld.
byte! eigen-kleur
; Bijhouden lege velden / gevulde velden
lege-velden: 320
forever [
; lees de invoer vanaf STDIN
invoer: to string! read/line
if invoer = "Quit" [break]
if invoer = "Start" [continue]
lengte-invoer: count invoer
if 1 = lengte-invoer [
eigen-kleur: to byte! invoer
continue
]
if 9 = lengte-invoer [
; Een zet op het bord gedaan: toevoegen
]
if 6 = lengte-invoer [
; Kleuren toegewezen
; bedenk zet
; Geef zet terug
]
]
BYE
We kunnen ons programma ook inzetten als programma om een spel te spelen en zo het verloop te analyseren. Mogelijk kunnen we ook verschillende strategieen implementeren en die tegen elkaar laten spelen.
Als we een zet kunnen bedenken en kiezen, kunnen we invoer genereren in plaats van inlezen. Wanneer er geen zet meer mogelijk is dan genereren we invoer 'Print' en drukken de verzamelde informatie af. In 'Print' genereren we tot slot de invoer 'Quit' om het programma te beeindigen.
Meta [
file: box1.meta
auteur: "Arnold van Hofwegen"
doel: {
Programma dat het spel Box kan spelen.
Meespelen met de codecup 2024/2025.
}
]
; Het speelveld of bord is de-box.
; Optie-h en optie-v zijn mogelijke plaatsen om een tegel te leggen
; verbod-h en verbod-v zijn plaatsen waar geen tegel meer past
let [de-box optie-h optie-v verbod-h verbod-v] binary! 320
change/repeat de-box 0 320
change/repeat optie-h 0 320
change/repeat optie-v 0 320
change/repeat verbod-h 0 320
change/repeat verbod-v 0 320
; maak een index om de gegevens te doorlopen groot genoeg aan
whole16! index
; Speler 1 of 2 heeft geen duidelijke rol, formaliteit
player: 2
; Eigen geheime kleur
byte! eigen-kleur
eigen-kleur: 0
let [vul-tegel tegel] binary! 6
change/repeat vul-tegel 0 6
change/repeat tegel 0 6
; Bijhouden aantal lege velden / gevulde velden
lege-velden: 320
; Scores
let scores binary! 6
change/repeat scores 0 6
; Beste zet is de index waar de beste zet moet komen
; Hoogste score is het best scorende resultaat (eigen score - scores andere kleuren)
whole16! beste-zet
whole16! hoogste-score
; index waarde naar lettercombinatie
letters-rijen= "ABCDEFGHIJKLMNOP"
letters-kolom= "tabcdefghijklmnopqrs"
; Ten behoeve van random voor zelftest
random/seed now
forever [
; lees de invoer vanaf STDIN
invoer: to string! read/line
if invoer = "Quit" [
break
]
if invoer = "Start" [
player: 1
continue
]
lengte-invoer: count invoer
if 1 = lengte-invoer [
eigen-kleur: to byte! invoer
continue
]
if 9 = lengte-invoer [
; Een zet op het bord gedaan: toevoegen
letter-rij: first-of invoer
advance invoer
letter-kolom: first-of invoer
advance invoer
for index 6 [
poke vul-tegel index to byte! copy cut invoer 1
advance invoer
]
horizontaal?: either "h" = copy cut invoer 1 [TRUE][FALSE]
vul-start: letter-rij - 65 * 20 + (letter-kolom - 96)
; Vul het bord, de-box en de opties en verboden
; Bereken de scores na deze zet van de tegenstander? (is dit nodig? je kunt er toch niks aan doen!)
continue
]
if 6 = lengte-invoer [
; Kleuren toegewezen
bewaarde-invoer: invoer
for index 6 [
poke tegel index to byte! copy cut invoer 1
advance invoer
]
beste-zet: 0
hoogste-score: -9999
; bedenk zetten en bepaal scores, volg een strategie(?)
for index 320 [
if all [0 < pick optie-h index
0 = pick verbod-h index][
; test op overlap, tel de overlap
; bij verbod dit toevoegen
; bewaar waardes als overlap > 0 en < 5
; voer zet uit op de-box
; bereken scores
; als zet beter dan voorgaande dan bewaar deze zet
; zet voorgaande waardes van de-box terug (neem zet terug)
]
if all [0 < pick optie-v index
0 = pick verbod-v index][
; test op overlap, tel de overlap
; bij verbod dit toevoegen
; bewaar waardes als overlap > 0 en < 5
; voer zet uit op de-box
; bereken scores
; als zet beter dan voorgaande dan bewaar deze zet
; zet voorgaande waardes van de-box terug (neem zet terug)
]
]
; Geef zet terug
; Als we een kleurencombinatie krijgen dan is er nog minstens 1 mogelijke zet
; Alleen in ons testprogramma is dat mogelijk anders
if beste-zet = 0 [
invoer: "Print"
continue
]
zet-rij: (beste-zet - 1) / 20 + 1
zet-kolom: 1 + remainder beste-zet 20
zet-h-of-v: "h"
uitvoer-zet: join/with join/with copy cut at letters-rijen zet-rij 1
copy cut at letters-kolom zet-kolom 1
; Doe de zet ook op het bord
; Voor testprogramma kan dit met
; invoer: join/with join/with uitvoer-zet bewaarde-invoer zet-h-of-v
; Schrijf de zet nu naar de andere partij
write/line join/with uitvoer-zet zet-h-of-v
continue
]
]
BYE
Het is nu een kwestie van alle lege plekken invullen.