Forum: Mikrocontroller und Digitale Elektronik How to: Snake


von Eumel (Gast)


Lesenswert?

Snake kennt ja hoffentlich jeder :)
Ich möchte dieses kleine Spiel auf einem Atmega48 mit einem 64*32 
Display reaslisieren.
Das Problem: Wie mache ich das möglichst ram freundlich?
Natürlich soll man das ganze Spielfeld mit der "Schlange" füllen können. 
Der Inhalt des Displays passt zwar komplett in das Ram des 
Atmega48(512byte Ram, 256 byte beschreiben das Display komplett) aber 
zum Snake Spiel gehören ja noch mehr Informationen. Nämlich in welcher 
Reihenfolge die Elemente der Schlange liegen. Also paktisch eine 
verkettete Liste.
Meine bisher ram freundlichste Idee: Startpunkt, Endpunkt und Anzahl der 
Elemente in einem der 32 universal Register speichern. Dann im Ram in 
jeweils 2 bit codiert die Richtung in der das nächste Element liegt. 
Damit passt die Schlange komplett in 512byte Ram....aber ich brauch noch 
nen bisschen Platz für den Stack und sonstiges Gedöns ;)
Hat irgendjemand ne bessere Idee?

von Eumel (Gast)


Lesenswert?

Und die einfachste Anwort: nimm nen Controller mit mehr Ram!!
Hab ich nicht da. Und ich bin arm.

von Clemens (Gast)


Lesenswert?

Elemente der Schlange größer als 1x1 Pixel machen? Dann noch etwas Rand?

von Marcus B. (raketenfred)


Lesenswert?

Das ist sehr heftig kalkuliert mit 256byte fürs Display!

Ich sage nicht, dass es unmöglich ist mit 512- ABER es ist wesentlich 
einfacher, wenn du einfach einen anderen Controller dir suchst^^

oder Externen Ram anschließen

von Eumel (Gast)


Lesenswert?

Hab ich schon, aus Sichbarkeitsgründen ist ein Element 2*2 Pixel groß 
(ist ein 128*64 Display)
Mit dem Rand ist natürlich eine Idee. Wobei ich mich da ja vor dem 
Problem drücke und es nicht löse ;) Eine elegantere Ram Nutzung wäre mir 
natürlich lieber.

von Chris (Gast)


Lesenswert?

Naja, es reicht eigentlich aus, wenn man jedes Feld durch ein Bit 
darstellt (Schlange auf Feld/Schlange nicht auf Feld). Zusätzlich 
müsstest Du die Position des Kopfes speichern und die Richtung, in die 
sich dieser im nächsten Schritt bewegen wird. Dazu kommt noch das 
Objekt, dass die Schlange fressen soll. Diese Informationen reichen aus.
Du musst dann nur in jedem Spielschritt die Schlange im Speicher 
"entlangparsen" und dabei selbst schauen, wo sie weiterverläuft. Das ist 
natürlich mit mehr Aufwand bzw. Rechenzeit verbunden, als mit deiner 
2-Bit-Richtungscodierung. Spart aber eben die Hälfte beim Speichern des 
Spielfeldes.

von DNS (Gast)


Lesenswert?

Chris schrieb:
> Naja, es reicht eigentlich aus, wenn man jedes Feld durch ein Bit
> darstellt (Schlange auf Feld/Schlange nicht auf Feld)

Unsinn, das reicht nicht! Man braucht auch die Reihenfolge.

von Eumel (Gast)


Lesenswert?

@Chris:
Ich verstehe nicht ganz was du meinst. Kannst du das nochmal 
anders/genauer erklären? Wo ist in deiner Speichermethode die Richtung 
enthalten? Woher weiß ich, welches Element gelöscht wird (klar, das 
letzte) und welches Element das neue letzte wird? Jede Bewegung besteht 
ja aus dem Löschen des letzen Elements und dem Anfügen eines neuen 
Elemts am Kopf. Das Futter lassen wir mal kurz außen vor...wobei das 
eigentlich nur dazu führt, dass das letzte Element einmal nicht gelöscht 
wird.

von Jobst M. (jobstens-de)


Lesenswert?

Also ich bekomme 5 (garantiert) bis 5,7 Pixel (Mittelwert) in ein Byte. 
Damit würdest Du mit 360-410 Bytes auskommen. Ich halte die 
Vorgehensweise aber für falsch.

Eigentlich müsste man eine Statistik darüber erstellen, wie lang im 
Schnitt die geraden Strecken sind und danach dann eine Kodierung 
festlegen. Denn ich denke es ist unwahrscheinlich, daß man jeden Pixel 
'abbiegt'.
Wichtig ist also die Länge zu kodieren, nicht jeden Pixel.


Folgende Kodierung halte ich für sinnvoll:
Paketgröße 4 Bit

Zwei Bit für Strecke:
1=1 Pixel, 3=3 Pixel, 0 Pixel bewegt man sich nicht, also 0=4 Pixel

Zwei Bit für Richtung:
00 = gerade
01 = links
10 = rechts
11 = auch diese Information sollte genutzt werden und bedeutet auch 
gerade, aber 4 Pixel mehr, als in Strecke angegeben. So ist eine Distanz 
von bis zu 8 Pixeln machbar.

Alternativ die 16 Kombinationen auch losgelöst von den Bits benutzen:
0-4 = 1-5 Pixel und links
5-9 = 1-5 Pixel und rechts
A-F = 1-6 Pixel und gerade

Diese Art findet von selbst kein Ende. Die Länge muß an einer anderen 
Stelle notiert werden.

oder:

:
A-E = 1-5 Pixel und gerade
F = Ende

Auch damit wirst Du vermutlich nicht über 6 Pixel pro Byte kommen. bei 
langen, geraden Strecken schon, aber wenn Du viel und schnell abbiegst 
nicht.

So ist Deine Schlange bei z.B. 100 Byte auf 200 Segmente mit jeweils 
max. 5 Pixeln (mit oder ohne Knick) begrenzt. Also bis zu 1000 Pixel 
lang, im Schnitt geschätzt nur ca. 60% davon.

Bei 256 Byte geschätzt bis zu 1500 Pixel.
Reicht vermutlich, das ist 3/4 des Spielfeldes.


Oooooder ...


Du benutzt 2 Bit pro Pixel direkt im FrameBuffer:
00 = Pixel leer
01 = Pixel gesetzt und Schlange biegt nach links
10 =                                       rechts
11 =                                       gerade

Dann sind zwar alle 512 Byte weg, dafür musst Du den Frame nicht jedes 
Mal neu zeichen. (spart Code)
Stack und Interupts entfallen dann allerdings, also alles pollen :-)
Alle anderen Infos musst Du dann in den Registern und in ungenutzten 
SFRs speichern.


Gruß

Jobst

von Peter D. (peda)


Lesenswert?

Du mußt erstmal den Startpunkt und die Anzahl der Elemente speichern (4 
Byte).

Und dann hast Du ein Array, wo die Richtungsänderung und die Anzahl bis 
zur Richtungsänderung gespeichert wird.
Die Anzahl als 6 Bit, damit reicht ein Byte. D.h. nach 63 Elementen 
erfolgt eine Dummy-Richtungsänderung.
Mehr als 256 Richtungsänderungen werden wohl kaum nötig sein.

Dann hast Du noch ein Array, wo das Futter gespeichert wird (20 Einträge 
sollte reichen).


Peter

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Du mußt erstmal den Startpunkt und die Anzahl der Elemente speichern (4
> Byte).
>
> Und dann hast Du ein Array, wo die Richtungsänderung und die Anzahl bis
> zur Richtungsänderung gespeichert wird.
> Die Anzahl als 6 Bit, damit reicht ein Byte. D.h. nach 63 Elementen
> erfolgt eine Dummy-Richtungsänderung.
> Mehr als 256 Richtungsänderungen werden wohl kaum nötig sein.
>
> Dann hast Du noch ein Array, wo das Futter gespeichert wird (20 Einträge
> sollte reichen).
>
>
> Peter

Für die Richtungsänderung müsste auch 1 Bit reichen:
- Die Schlange kann nur 3 Richtungen links, geradeaus & rechts.
- kodiere man nun wie Peter vorgeschlagen hat die Länge eines Teilstücks 
in 6 Bits haben ich 64 Möglichkeiten, nun hat Eumel das Spielfeld auf 
64*32 begrenzt. Wenn also das Teilstück 63 lang ist, beist sich die 
Schlange beim nächsten Zug selbst.
-- Also kann ich mir die Richtung geradeaus sparen und es bleiben nur 
noch links & rechts übrig.

Das 7. Bit könnte man nutzen um zum Beispiel 2 kurze Teilstücke (>=6) in 
einem Byte zu kodieren, so spart man wieder etwas RAM.

Ausserdem würde ich wie bei einem String das Array mit 0 terminieren, 
d.h. 0 = Ende der Schlange.

Oder habe ich da noch was übersehen?

von Roland P. (pram)


Lesenswert?

Wieviel Speicher hat denn das Display, vielleicht kannst du etwas im 
CG-RAM o. ä. auslagern.

Gruß
Roland

von Ingo (Gast)


Lesenswert?

Mal ganz ehrlich, in Zeiten, wo ein AVR nur n paar Euro kostet, ist doch 
sowas sehr unsinnig, wenns ein Einzeslstück ist.

Es sei denn, dich interessiert hier wirklich wie man es kleiner bekommt 
und nicht wie DU es in den AVR bekommst.

Guck mal hier:
http://www.atmel.com/dyn/products/param_table.asp?category_id=163&family_id=607&subfamily_id=760

Da gibts zig AVR mit 32Pins und mehr als 512B RAM. Mega168 z.B.

Ich finde, gerade bei Neuentwicklungen sollte man bei sowas erstmal 
nicht geizen, dann bleibt immer noch genug Platz für Erweitungen.


Grüße,
Ingo

von Purzel H. (hacky)


Lesenswert?

Was fuer'n idiotischer Einfall. Fuer ein Stueck. Man spart die 5 Euro 
fuer einen Mega644, und blaest dafuer Tage raus, um einen 
RAM-Sparalgorithmus zu finden...

von M. K. (avr-frickler) Benutzerseite


Lesenswert?

Ingo schrieb:
> Ich finde, gerade bei Neuentwicklungen sollte man bei sowas erstmal
> nicht geizen, dann bleibt immer noch genug Platz für Erweitungen.

Hehe sehe ich auch so, hab mir letztens ein Board mit ATMega2560 und 
ext. RAM gemacht. Das Projekt dazu ist in der Grundform so gut wie 
fertig, mit reichlich Debug-Strings gerade einmal 64K groß.
Den Rest kann ich nun für schöne Spielereien verwenden ;)

von Dimpflmoser (Gast)


Lesenswert?

Hi,

ich hatte mal vor ~10 Jahren einen Snake Clone auf einem graphischen 
Taschenrechner programmiert. 512 byte sind schon ziemlich mager.

Meine Lösung war damals:
- ein Array mit 250 Zeilen und 2 Spalten.
- eine Countervariable
- eine Variable, die die Schlangenlänge dargestellt hat.

Die Countervariable startete bei 0 und es wurde dann die x & y 
Koordinaten in die Counterzeile des Arrays geschrieben.
Wenn man bei 249 angekommen war, wurde der Counter auf 0 zurück gesetzt.
Damit die Schlange eine definierte Länge hatte, wurde immer das altuelle 
Pixel des Bildschirms gesetzt und dann die Koordinaten des Pixels, der 
gelöscht werden muss über die Tabellenzeile Counter-Schlangenlänge 
geholt und gelöscht.
Wenn der Akuelle Pixel auf deiner Frucht oder was auch immer gelandet 
ist, wurde die Schlangenlängenvaribale um 2 erhöht.

Natürlichen war noch ein Ansteuerungsanteil für die Richtungen, eine 
Kollisionsabfrage und ein wenig Spagetticode dabei. Auserdem musste 
darauf geachtet werden, dass beim Array Überlauf das Schlangenende nicht 
bei der Zeile -10 sondern bei 240 war.

Das Programm hatte den Nachteil, dass wenn die 250 Zeile verbraucht 
waren, die Schlange sich fröhlich abgelöst hatte und eine neue gestartet 
hatte.

Vielleicht kannst du das Array mit den Koordinaten optimieren, es gibt 
ja nur 64*32 = 2048 Möglichkeiten. Also würden für die Koordinaten 
theoretisch 11 Bit reichen.

Hoffendlich verständlich und ein paar interessante Infos dabei.

Gruß,
Dimpflmoser

Evtl gibt es auch noch eine Möglichkeit von der aktuellen Koordinate auf 
das Schlangenende zurückrechnen zu können, in dem man bei jedem 
Richungswechsel in ein Array einmal ein Bit setzt für 0=rechts 1=links 
und in einem Zweiten Array die Länge des geradeausfahrens speichert.Wenn 
allerdings ständig links und rechts gefahren wird, ist auch dafür ein 
großes Array nötig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Eine Skizzierung wie man das Spielfeld darstellen kann ist in:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=901101#901101

Die Schlong wird in Segmente unterteilt; jedes Segment passt locker in 1 
Byte:  1 Bit für die Richtung und 7 Bits für Segmentlänge.

von Eumel (Gast)


Lesenswert?

erstmal vielen Dank für die tollen Vorschläge :)
Mal sehen ob mir selbst noch was einfällt, um das ganze Spielfeld mit 
der Schlange zu füllen, egal wie deren Form ist. Bei einer sehr langen 
und verwinkelten Schlange würden ja die vorgeschlagenen Methoden 
versagen.
Vielleicht nehm ich auch einfach den lahmen Ansatz und verkleiner das 
Spielfeld durch einen Rand. Wären dann 465 byte für die Schlange...47 
byte für den Stack. Das sollte reichen.

von Jobst M. (jobstens-de)


Lesenswert?

Also wenn Du wirklich daran festhältst, für jeden Pixel die Richtung zu 
notieren und dies auch für die komplette Füllung des Feldes vorsiehst, 
dann sind das 2048 Informationen mit je drei Zuständen (3^2048). Diese 
Information kannst Du in 406 Byte unterbringen (2^(406*8)).
Das sind 5,044 Pixel pro Byte.

Da wäre ich dann mit meiner Lösung, 5 Pixel in ein Byte zu bringen schon 
recht dicht dran. 3^5 Bekommst Du in ein Byte.

Dann hast Du weitere 55 Byte Platz :-)

Aber bedenke, daß Du die Schlange auch malen und zum Display 
transportieren musst, das 'Bild' also irgendwie zumindest in Stücken im 
RAM haben musst.


Gruß

Jobst

von H.Joachim S. (crazyhorse)


Lesenswert?

Und ich bin so froh, dass ich nicht mehr mit jedem Bit oder Byte geizen 
muss...
Es gab Zeiten, da musste ich das. Und es ergab sich auch eine gewisse 
Befriedigung, scheinbar unmögliches doch möglich gemacht zu haben.
Heute ist es für mich effektiver, nahezu kostenlose Ressourcen zu nutzen 
und sich die Zeit zu sparen.

von Eumel (Gast)


Lesenswert?

Joachim, das ist mir klar. Aber ich mach das ganze ja in keinster weise 
beruflich sondern nur aus privatem Vergnügen, da sei mir die Frickelei 
gegönnt :)

von Lehrmann M. (ubimbo)


Lesenswert?

Haben die Herrschaften mal daran gedacht, dass ein Display auch in 
gewisser Weise ein Speicher ist der ausgelesen werden kann?

von Purzel H. (hacky)


Lesenswert?

Wenn der Poster die Schlange nicht selbst hinkriegt, ist das GrafikRAM 
des LCD-Controller jenseits der Moeglichkeiten.

von Stryker (Gast)


Lesenswert?

Darf die Länge der Schlange begrenzt werden? Ich kenne zumindest eine 
Version, wo die Schlange nach dem 30. Apfel nicht mehr wächst. Oder 
alternativ nach dem 10. Apfel das Level gelöst ist.

Wäre dies möglich, so lässt sich die Schlange in O((n*c)/2+2) byte 
kodieren:

- nach einer horizontalen ausrichtung folgt eine vertikale und vice 
versa
- ein byte enthält die endkoordinate
- ein byte die länge der schlange
- die (n*c)/2 Bytes sind Eckkanten dabei muss wie ein Vorposter bereits 
schon beschrieb abwechselnd nur die x und y Koordinaten geschspeichert 
werden. das c kommt von der möglichen anzahl der Richtungswechsel pro 
Verlängerung.

Durch diese Art der kodierung ist so auch ein Multiplexen z.B. leicht 
realisierbar (organisation in Spalten und Reihen).

Bei einer Schlange mit 30 Verlängerungen und 5Pixeln "Verlängerung" 
komme ich so auf 30*5/2 + 2 auf 77 Bytes für die Schlange... ähnlich 
kann man dann auch die Level designen... dann ist noch genug Platz für 
Scoring, Stack etc...

Desweiteren: Das Display wurde schon 2 mal angesprochen: Falls der Ram 
auslesbar ist und vllt noch einen Framebuffer hat kommst du so schnell 
an extra Ram...


Und nun noch meine kleine private Meinung hinsichtlich Resourcen und den 
sinnigen Einsatz: im privaten Einsatz, und wenn die Resourcen eh schon 
da sind ist es legitim sie zu nutzen, jedoch wenn SW weitergegeben wird 
(z.b. eine Lib) sollte man penibelst darauf achten, wie man damit 
umgeht... das ist mitunter die schlimmste Seuche der Informatik. 
Programme sind immer mehr schichten basierend und wenn man schon in der 
untersten ebene speicherplatz verschwendet und dieses in jeder iteration 
zusätzlich draufgeht, so muss man sich nicht wundern, wenn ein Programm 
was eine Zahl hochzählen lässt mal eben ein MB im Speicher frisst... 
aber ok - das ist nicht umbeding ein µC Thema ;-)

von Jobst M. (jobstens-de)


Lesenswert?

@H.joachim Seifert: Sieh es wie Kreuzworträtsel lösen :-)

@Lehrmann Michael: Ich habe hier ausgerechnet 128x64 LCDs liegen, die 
man nur optisch auslesen kann. Elektrisch kann man nur darauf schreiben.
Das DOGM128 gehört auch dazu.


Gruß

Jobst

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.