Code Golfing – size coding
Die Demoscene ist eigentlich Teil der grösseren Bewegung des Code-Golfings. Code Golfing ist ein Wettbewerb zur Frage, wie kann man* ein Problem (etwa den kleinsten Compiler, was zu FALSE, Brainfuck und Co führte) mit möglichst wenig Code in einer spezifischen Umgebung lösen (oder wie schaffe ich es eine Umgebung zu gestalten in der Probleme mit möglichst wenig Code gelöst werden können). Konkreter: Was ist der kleinstmögliche Code für ein Problem oder sportlicher: Mit wie wenig Schlägen komme ich ans Ziel. Mehr zu Codegolfing hier >
Demoscene: Feste Grösse, Golfen mit dem Resultat
Die Crackerszene des 8- und 16-Bit lebte vom Dilemma sich darstellen zu wollen und gleichzeitig wenig Platz zu haben (Die Disketten waren schon so rand voll mit Spielinhalten). Was zu interessanten Intros führte in denen die Cracker* alle funktionalen Inhalte wie Softwareübersichten, Auswahl, Trainer (Cheatmodes), Infos, Kommunikationswege und Werbung über sie (Postadresse, BBS), Greetings, Hatings möglichst kreativ verpackten in Texte, Scrolltexten, Spielen mit Schriften, Effekten. Es war also ein weiterer Wettbewerb entstanden: Wer kann wie viel mit wenig Platz. Dadurch wurden auch die teilweise ‘qualitativ’ schlechten Spiele wenigstens in ihren Intros etwas Grosses.
Die Demoscene, die sich bekanntlich aus den begrenzten Ressourcen der Crackerszene entwickelte, hat für die Contest Kategorien geschaffen und damit die Ressourcen beschränkt 8byte, 64byte, 256byte (historische Kategorie wegen den ), 512b, 64kb etc. So dass es weiterhin heisst: Wer kann welche Effekte, Demos, Stories in minimalste Anzahl von Programm verpacken. Und damit alle die gleichen ‘ Chancen’ haben – begrenzt man die Kategorien. Ein Beispiel dafür wäre etwa: LoveBytes/Kategorien.
Neben den klassischen Computern (C64, Spectrum, Atari ST, Amiga, Windows) und Consolen (Atari 2600) haben seit längerem Fantasy Consolen/Computer Einzug gehalten wie Tic80 oder Pico8. Mehr dazu hier >
Gamedesign-Golfing, Nanowettbewerb
Zunehmend taucht nun in Demosceneevents auch die Kategorie «Nanogames» auf. Meist sind sie dann – blöderweise in der selben Kategorie – wie Demos. Etwa 256b Games. Dabei wird wenig Rechnung getragen, dass eine Spielechanik meist auf if und loops Bedingungen aufbaut. Vermutlich wäre eine 384 Byte Untergrenze etwa für Fantasy Consolen sinnvoll. Nichts desto trotzt führt das zur Frage: Was lässt sich in so wenig Bytes machen? Und die Frage dahinter: Was ist Gamedesign Golfing? Was ist das Verhältnis von Code und Game von den Visuals bis zur Mechanik. Und vielleicht interessanter: Was sind die kleinstmöglichen Spielmechaniken für ein Genre?
Im Nachfolgenden soll anhand des Lovebyte-Contest 2023 diesen Fragen an der Oberfläche nachgegangen werden.
Constraints – Grösse ist alles
Eigentlich sollte man jeden Contest von hinten anschauen. Was sind die Bedingungen? Wo ist der Contest beschränkt. Hier gibt es zwei unterschiedliche Kategorien:
File-Size-Grösse
Die historische Grösse ist einfach, sie ist einfach die Grösse der ausführbaren Datei (etwa exe). Das ist einfach messbar und überprüfbar. Selbstverständlich gibt es auch hier Systeme, die schlecht wegkommen. Etwa Macos X. Hinter dem ausführbaren Code lassen sich aber auch viele Source-Code Bytes verstecken, der Code lässt sich Packen etc.
Heutige FantasyConsolen ermöglichen es aber auch den Code einfach gezipped zu exportieren und damit das eine oder andere Byte noch loszuwerden. Pico8 verwendet dafür in der Console eine Art ZLIB:
Export -t NAMEX.p8.rom
Die meisten Contests verwenden diese Form. Hier stellt sich die Frage, ob man dann nicht auch für diesen Packer optimiert.
Source-Code-Grösse
Hier wird nur die Grösse des effektiven Codes gezählt. Dieser lässt sich dann auch einfach kopieren. Beliebtes Format hier sind die TweetCards 280 Zeichen. Diese lassen sich per Tweet verschicken und einfach schnell ausprobieren.
Oldschool & Fantasie-Konsolen
Zuerzeit dominieren bei den Fantasy-Consolen Pico8/Tic80. Daneben gibt es allerdings diverse andere Konsolen. Auch stark vertreten ist Assembler.
Konzepte und Games
Games sind prinzipiell komplexe Software. Sie sind meistens multimedial (Displays: Visuell, Sound) und interaktiv (Interfaces, Entscheidungen). Zusätzlich verwenden sie Spielmechaniken. Was mehrheitlich zu Loops und Entscheidungen und damit zu vielen Abfragen führen.
Es ist deswegen wichtig im Bereich Nanogames, die Konzepte so einfach wie möglich zu halten, bzw. sie auf das minimalste zu reduzieren. Das heisst auch: Wie kann ich etwa Loops mehrfach verwenden. Wie kann ich inits direkt in eine Schlaufe während des Gamloops integrieren?
Umgekehrt gefragt; was kann ich alles in einem Loop unterbringen.
Probleme werden oft in Games im Allgemeinen gelöst. Hier ergibt sich bei der Überarbeitung auch oft die Frage, lässt dich das Ganze nicht als Spezialfall lösen? Muss x,yz sein?
Beispiele:
Double Us
Alternative Möglichkeiten für Ressourcen, Statusdarstellungen:
Game Over
Interessante Funktionen (anders als in OldSchool Demoscening):
- Floating-Point
- Flr()
- Ceil()
- ^
- a/10
- a\10
- a%10
Floating ermöglichen auch all die 3D Dinge!
String:
Concat ..
length #a
Sub: String fangen bei 1 an! (substring)
Speicherung: peek & poke …
// cstore and co
- store to the cardtriged …
Pico8 (FantasyConsoles)
Jede Programmiersprache hat ihre Eigenheiten und ermöglicht verschiedene Dinge. Zumindest 2 der FantasyConsoles verwenden Lua als Scriptsprache (Es ist vergleichweise einfach Lua zu implementieren und als VirtualMaschine laufen zu lassen). Hier soll auf Lua eingegenagen werden.
Eine gute Einführung ins Demoscening findet sich auch hier:
https://demobasics.pixienop.net
Die folgenden Tipps zusammengesetzt aus Selbstverständlichkeiten, Tipps des Tetris-Beispiel und eigene Erfahrungen: https://demoman.net/?a=optimizing-for-tweetcarts
Effects
Klassische Demoscene Effekte
3D
Schritt 0: Iterationen
Regelmässig testen und ausprobieren. Gerade das «Export -t xyz.p8.rom» erzeugt durch die Zlib-Kompression immer wieder andere Zeichenzahlen. Da kann man auch etwas entfernen und es hat auf einmal mehr Zeichen, da es nun nichts mehr zu packen gibt.
Schritt 1: Mainloop
Einfachster Mainloop:
function _draw()
end
Noch einfacherer Mainloop (ohne Geschwindigkeitskontrolle):
::_::
goto ::_::
Alle Pixel durchgehen:
Alle Pixel 2:
Zeichnen und Verwlaten im selben loop!
Schritt 2: Vereinfachen
Einfachere Schreibweisen:
i=i+1 > i+=1 // -2
i+=-1 > i-=1 // -1
i+=0.1
i+=.1
Verwenden von Buchstabenvariablennamen:
abc=1 > a=1
Schritt 3: Oft verwendete Teile als Variable
i+=128
t=a*128
h=128
i+=h
t=a*h
— // + 5 – 6
Dasselbe gilt natürlich auch für Zwischenresultate:
h=t/6
Je öfter man das verwendet, umso mehr lohnt es sich.
Kann man eventuell auch dieselben Zahlen nochmals benutzen? Etwa bei Effekten?
Funktionsnamen ersetzen
a=sin(t/100)
b=sin(l/100)
s=sin
a=s(t/100
b=s(t/100)
— // +5 -6
Je öfter man nun sin verwendet, umso besser. Cos ist auch nichts anderes als 1-sin etc!
Schritt 3: If-statements
Leider benutzt LUA kein {} sondern
if() then else end
if () then end
> Trick if a==0
If (i==0)
if (i<1) –// -1
Kaskadieren von If-Statements
if()CMD1cmd2CMD3
Schritt 4: Function-Default-Statements
Viele Funktionen lassen sich auch ohne Werte nutzen.
Mget(4,0)
mget(4) — // -1
mset(x,y,0)
mset(x,y) — // -1
Schritt 4: Reihenfolge
Immer wieder interessant ist die Frage, lässt sich mit Umstellen und Commandoaneinderreihen nicht doch noch etwas sparen.
Schritt 4: Darstellung
Dinge können auf verschiedenste Arten dargestellt und kombiniert werden:
- Direkte Zeichenroutinen (Vorteil direkt: circ(),rect … brauchen viel
- Sprites (spr)
- Sprites (sspr)
- Text bzw. vordefinierte Sonderzeichen (Herzen etc.)
- Tilemaps
- Memorymanipulationen
?,1,7
// leuchtender
// copy screen to background
memcpy(0,0×6000,1000)
Schritt 5: Kreativität
Nicht zu unterschätzen, ist die eigene Kreativität und das immer wieder über den Code schauen.
– Gibt es etwas, was man anders machen könnte?
– Gibt es etwas
– Wenn man etwas umstellt, verändert sich etwas? Wird etwas neu möglich?
Release
# pico8 – capturing_on_mac (1280×720) # XYZ = your user name terminal drag in your Pico8-App-Icon cd PICO-8.app/Contents/MacOS ./pico8 -width 1280 -height 720 # Use your screencapturing tool on the pico 8 window oder make the window bigger and screenshot exactly 1280×720
Beobachtungen
// negative values und abstanzberechnung
// immer die möglicherweise negativen beim negativ benützen
x-mouseposition.x
// demofix
einfang spiel
einfacher nur überleben kein einsammeln oder auslassen
!!!!
// demofix game
// no mset > direct array 32*32 …
// display cls
// breakout
?“●“..s,a,b,t
FIX the PIX
// val/2
g,s=0t=0p=16s=stat
j=128d=rnd
poke(0x5f2d,1)::_::h=mget
m=s(32)n=s(33)e=0for x=0,j do
for y=0,j do
v=h(x\4,y\4)f=sin(xyt/20)*2
if(v>0)e+=1f=7
pset(x,y,f)
end
end
t+=1if(t%p<1)mset(d(b),d(b),d(2)) v=h(m\4,n\4)if(v>0)v=0t=0
mset(m\4,n\4,0)
if(e>50)cls()
?“웃“..(t\p),m,n,7
::::h flip()goto
pico-demo-checklist
effects
/*
x,n: 0-128
y,m: 0-128
o=n\8
p=n\8
// circles …
f=2+(m^2+n^2)(t\3)%3
// schachbrett
f=m\t
// simplest loop
h=64i=0::::i+=1pset(i%h,i\h%h,i%9)goto
// flip() nicht benutzt
// optimising to then end
defaults … verkürzt
// tile nutzen …
s=mset
g=mget
s(0,1)
g(1) // gibt den 5,0 wieder
pset(x,y) // setzt pixel auf 0!
mget(x ) >>?
demo short – loop
h=99i=0::::i+=1pset(i%h,i\h,i%7)goto
longer
l=0h=99i=0::::i+=1pset(i%h,i\h,flr(l\i))l+=1if(i>h*h)i=h goto
worst – smaller size …
(a==0) >> better (a<1)
*/
- no space
- a,b=0
- s=stat
- a
b
ab
) / etc
- ? :
- a%b & a\b
- if(v>0) v
- if (x)AND(y)
if (v) then is not working:-()
if (i==0) > if i<1
32 > zahl
Map(0,0) > map
another place
?
poke() > poke 0! 1
// faster
poke(24365,1)
strategies
rnd > endless game
% instead of random
btn([left])
btn(<)
btn(righ)
// take always one !
use a timer and than %30 objects
over t%
print ?
?“웃“..(t/10)..“ „..a,m,n,7
if() >>>>>
if a=1NEXT COMMAND!
if(a==3)CMD1CMD2 CMD3
if()
not working
a,b,c=0 !!! > a,b,c=0,0,0
::::h flip()goto
mehrfachverwendete zahlen?
x=32
32
32
spr > circ?
circ default no radius
spr(1,a,b)
circ(a,b,3)
werte: auch 0.5 möglich !!!!
if(a>7)CMD > if a>7CMD
if(a>7)CMDX CMDY else CMDX,CMDY
a = -1
if (a) not
a = 1
if (a) ys
print chr(7) … > ton
strategies:
- zeichnen und verwaltung im selben loop!
- double use!
- atari 2600 use
- wiederverwendetes in variable
Attention
memcpy( destaddr, sourceaddr, len )
- DEST 2. SOURCE!
SPRITESCREEN TO SCREEN
— copy sprite sheet to screen
memcpy(0x6000,0,1000)
— screen to sprite screen
memcpy(0,0×6000,1000)
// print colors background …
https://pico-8.fandom.com/wiki/P8SCII
https://pico-8.fandom.com/wiki/P8SCII_Control_Codes
WIDE
S T R I PED
Dotted: ^
\^w : Wide. Doubles the width.
\^t : Tall. Doubles the height.
\^= : Stripey. When wide or tall, only even pixels are rendered.
\^p : Pinball. Equivalent to wide, tall, and dotty.
\^i : Inverted. The foreground color is used as the background, and vice versa.
\^b : Bordered. Text has left and top padding. (On by default.)
\^# : Solid background. The default is a transparent background.\f P0
: foreground color\# P0
: background color
print(„#4 \^w font“)
SCREEN-CAPTURING:
cd /Users/XYZ/Desktop/Themen/pico-8/pico-8-4/PICO-8.app/Contents/MacOS
./pico8 -width 1280 -height 720
./pico8 -width 1380 -height 750
PICO-ROUTINEN
// direct in
// grid over it ….
pset(x,y,(t+l)%5)
for i=0,128128,2 do n=i%128 m=i\128 pset(n,m,((n+t/4)m)%7+12)
end
memory: https://trasevol.dog/2017/04/21/di13-spritesheet1/
interesting: https://trasevol.dog/2017/04/13/doodle-insights-12-voxels-in-pico-8/