KI-Wettbewerb, OOP-Framework

Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Neue Antwort erstellen

Smily

Betreff: KI-Wettbewerb, OOP-Framework

BeitragMo, Jan 11, 2010 15:16
Antworten mit Zitat
Benutzer-Profile anzeigen
wie hier angekündigt, habe ich mir mal erlaubt, die Idee eines KI-Wettbewerbs nach Blitzmax zu portieren und da die vorzüge von OOP zu nutzen.

Update 0.1: Respawn-System (Danke an ChaosCoder)

Als erstes der Code, für die Kopierfaulen unten noch ein Link zu einer Zip-Datei, welche alle 4 dateien enthält:
gameBase.bmx
BlitzMax: [AUSKLAPPEN]
SuperStrict
Const ACCELERATION:Float = 0.1
Const BULLET_DAMAGE:Int = 1
Const BULLET_DELAY:Int = 1000
Const BULLET_SPEED:Int = 6
Const DISTANCE_TO_HIT:Int = 8
Const MAX_SPEED:Int = 4
Const MAX_SPEED_BACKWARD:Int = 2
Const MAX_TURN_SPEED:Int = 3
Const PLAYER_HEALTH:Int = 3
Const TIME_RESPAWN:Int = 1000


Type TGameField
Field player:TList = CreateList()
Field width:Int
Field height:Int
Field bullets:TList = CreateList()
Method update()
For Local p:TPlayer = EachIn player
If p.isDead() And p._deadtime+TIME_RESPAWN < MilliSecs() p._respawn()
If p.isDead() Continue
p._unlock()
p.update()
p._update()
p._lock()
If p._x < 0 p._x = 0
If p._y < 0 p._y = 0
If p._x > width p._x = width
If p._y > height p._y = height
Next

For Local b:TBullet = EachIn bullets
b._update()
If b._x < 0 Or b._y < 0 Or b._x > width Or b._y > height bullets.remove(b)
Next

For Local b:TBullet = EachIn bullets
For Local p:TPlayer = EachIn player
If p.isDead() Continue
If Sqr((b._x-p._x)^2 + (b._y-p._y)^2) <= DISTANCE_TO_HIT And Not (b._orig = p)
bullets.remove(b)
p._health:-BULLET_DAMAGE
If p._health <= 0 p._die(b)
End If
Next
Next
End Method

Method setSize(w:Int, h:Int)
Self.width = w
Self.height = h
End Method

Method addPlayer(p:TPlayer)
Self.player.addLast(p)
p._field = Self;
End Method

Method addBullet(b:TBullet)
Self.bullets.addLast(b)
End Method
End Type

Type TPlayer Abstract
Field _locked:Int=True
Field _health:Int = PLAYER_HEALTH
Field _x:Float
Field _y:Float
Field _ri:Float
Field _speed:Float
Field _field:TGameField
Field _lastshoot:Long
Field _turnlock:Int = False
Field _speedLock:Int = False
Field _kills:Int = 0
Field _dead:Int
Field _deadtime:Int
Field _deaths:Int
Method _update() Final
_x:+Cos(_ri) * _speed
_y:-Sin(_ri) * _speed
End Method

Method _lock() Final
_locked=True
End Method

Method _unlock() Final
_locked=False
_turnlock=False
_speedlock=False
End Method

Method _respawn() Final
_dead = 0
_health:Int = PLAYER_HEALTH
_x = Rand(0, 550)
_y = Rand(0, 550)
_ri = Rand(0, 360)
_speed = 0
respawn()
End Method

Method _die(bullet:TBullet) Final
bullet._orig._kills:+1
_Deaths:+1
_dead = 1
_deadtime = MilliSecs()
End Method

Method getX:Float() Final
Return _x
End Method

Method getY:Float() Final
Return _y
End Method

Method getRi:Float() Final
Return _ri
End Method

Method getSpeed:Float() Final
Return _speed
End Method

Method getKills:Int() Final
Return _kills
End Method

Method getDeaths:Int() Final
Return _deaths
End Method

Method getScore:Int() Final
Return getKills()-getDeaths()
End Method

Method getHealth:Int() Final
Return _health
End Method

Method isDead:Int() Final
Return _dead
End Method

Method getOtherPlayers:TList() Final
Local ret:TList = _field.player.copy()
ret.remove(Self)
Return ret
End Method

Method getBullets:TList() Final
Return _field.bullets.copy()
End Method

Method update() Abstract

Method turn(r:Int) Final
If _locked Or _turnlock Return
If Abs(r) > MAX_TURN_SPEED r = MAX_TURN_SPEED * Sgn(r)
_ri:+r
_ri = (_ri + 360) Mod 360
_turnlock = True;
End Method

Method speedUp() Final
If _locked Or _speedlock Return
If _speed>=MAX_SPEED
_speed=MAX_SPEED
Return
End If
_speed :+ACCELERATION
_speedlock = True
End Method

Method speedDown() Final
If _locked Or _speedlock Return
If _speed <= -MAX_SPEED_BACKWARD
_speed = -MAX_SPEED_BACKWARD
Return
End If
_speedlock=True
_speed :-ACCELERATION
End Method

Method break() Final
If Abs(_speed) < ACCELERATION _speed = 0
If _speed > 0
speedDown()
Else If _speed < 0
speedUp();
End If
End Method

Method shoot() Final
If _locked Return
If(MilliSecs()-_lastshoot) < BULLET_DELAY Return;
Local b:TBullet = New TBullet
b._orig = Self
b._x = _x
b._y = _y
b._ri = _ri
b._speed = BULLET_SPEED
_field.addBullet(b)
_lastshoot = MilliSecs()
End Method

Method respawn()
End Method

End Type

Type TBullet
Field _x:Float
Field _y:Float
Field _ri:Float
Field _speed:Int
Field _orig:TPlayer

Method _update() Final
_x:+Cos(_ri) * _speed
_y:-Sin(_ri) * _speed
End Method


Method getOrig:TPlayer() Final
Return _orig
End Method

Method getX:Int() Final
Return _x
End Method

Method getY:Int() Final
Return _y
End Method

Method getRi:Int() Final
Return _ri
End Method

Method getSpeed:Int() Final
Return _speed
End Method
End Type

Type TGameRenderer Abstract
Field gameField:TGameField
Method draw(x:Int, y:Int) Abstract
End Type


SampleRenderer.bmx
BlitzMax: [AUSKLAPPEN]
Import "GameBase.bmx"
SuperStrict

Type TPlayerToColor
Field player:TPlayer
Field r:Int
Field g:Int
Field b:Int
End Type

Type TSampleGameRenderer Extends TGameRenderer
Global colors:Int[] = [255,0,0, 0,255,0, 0,0,255, 0,255,255, 255,0,255, 255,255,0]
Field colorMap:TList = CreateList()
Field colorIDX:Int = 0;
Method draw(x:Int, y:Int)
SetOrigin x,y
SetColor 255, 255, 255
DrawLine 0, 0, gameField.width, 0
DrawLine 0,0,0,gamefield.height
DrawLine 0,gamefield.height,gamefield.width,gamefield.height
DrawLine gamefield.width, 0, gamefield.width, gamefield.height
Local i:Int
For Local p:TPlayer = EachIn Self.gameField.player
setColorForPlayer p
DrawText p.getKills(), -85, 0+i*16
DrawText p.getDeaths(), -55, 0+i*16
DrawText p.getScore(), -25, 0+i*16
' DrawText " - =", -85, 0+i*16
If Not p.isDead()
Local px1:Int = p._x+Cos(p._ri )*10
Local py1:Int = p._y-Sin(p._ri )*10
Local px2:Int = p._x+Cos(p._ri - 150)*10
Local py2:Int = p._y-Sin(p._ri - 150)*10
Local px3:Int = p._x+Cos(p._ri + 150)*10
Local py3:Int = p._y-Sin(p._ri + 150)*10
DrawLine px1, py1, px2, py2
DrawLine px2, py2, px3, py3
DrawLine px3, py3, px1, py1
SetColor 255,0,0
DrawRect p._x-10, p._y-25,20,5
SetColor 0,255,0
DrawRect p._x-10, p._y-25,20.0/PLAYER_HEALTH*p._health,5
End If
i:+1
Next


For Local bt:TBullet = EachIn gamefield.bullets
setColorForPlayer bt._orig
DrawOval bt._x-2, bt._y-2, 4, 4
Next
SetOrigin 0,0
End Method

Method setColorForPlayer(p:TPlayer)
For Local ptc:TPlayerToColor = EachIn colorMap
If ptc.player=p
SetColor ptc.r, ptc.g, ptc.b
Return
End If
Next
Local ptc:TPLayerToColor = New TPlayerToColor
ptc.player = p
ptc.r = colors[colorIDX*3]
ptc.g = colors[colorIDX*3+1]
ptc.b = colors[colorIDX*3+2]
SetColor ptc.r, ptc.g, ptc.b
colorMap.addLast(ptc)
colorIDX = (colorIDX +1) Mod 6
End Method
End Type


SampleKI.bmx
BlitzMax: [AUSKLAPPEN]
SuperStrict
Import "GameBase.bmx"
Type THumanPlayer Extends TPlayer
Method update()
If KeyDown(KEY_LEFT) turn(+4)
If KeyDown(KEY_RIGHT) turn(-4)
If KeyDown(KEY_UP)
speedUp()
Else If KeyDown(Key_DOWN)
SpeedDown()
Else
break()
End If
If KeyDown(KEY_SPACE) shoot()
End Method
End Type

Type TSimpleKI Extends TPlayer
Method update()
Local enemy:TPlayer
'break()
For Local p:TPLayer = EachIn getOtherPlayers()
If p.isDead() Continue
enemy = p
Exit
Next

If enemy=Null
break()
Return
End If

Local riToSet:Int = (ATan2(getY()-enemy.getY(), enemy.getX()-getX()) + 360) Mod 360
Local ldiff:Int = (riToSet-getRi() +360) Mod 360
Local rdiff:Int = (getRi()-riToSet +360) Mod 360
If(ldiff < rdiff)
turn(ldiff)
Else
turn(-rdiff)
End If
Local ridiff:Int = Min(ldiff,rdiff)
Local pdiff:Int = Sqr((getY()-enemy.gety())^2 + (getX()-enemy.getX())^2)

If pdiff > getSpeed()*20 + 20
If(ridiff) = 0
speedUp()
Else If (ridiff) < 50
If getSpeed() < ridiff/10 speedDown() Else speedUp()
Else
speedDown()
End If
Else
break()
End If

If ridiff<=1 shoot()
End Method
End Type


Start.bmx
BlitzMax: [AUSKLAPPEN]
SuperStrict
Import "GameBase.bmx"
Import "SampleRenderer.bmx"
Import "SampleKI.bmx"
SeedRnd MilliSecs()
Local gameField:TGameField = New TGameField
Local renderer:TGameRenderer = New TSampleGameRenderer
Rem
Local p1:TPlayer = New THumanPlayer
p1._x = Rand(0,550)
p1._y = Rand(0,550)
p1._ri = Rand(0,360)
End Rem

gameField.addPlayer(New THumanPlayer)

For Local x:Int = 1 To 3
Local p2:TPlayer = New TSimpleKI
p2._x = Rand(0,550)
p2._y = Rand(0,550)
p2._ri = Rand(0,360)
gamefield.addPlayer(p2)
Next
gamefield.setSize(550,550)
renderer.gameField = gameField;

Graphics 800,600

Repeat
If(AppTerminate()) End
gameField.update()
Cls
renderer.draw(125,25)
Flip
Until KeyHit(KEY_ESCAPE)


Zip-Datei

Downloaden und alles in ein verzeichniss entpacken. die "start.bmx" sollte direkt lauffähig sein.

Ein paar Erklärungen:

in gameBase.bmx befindet sich alles, was für das eigentliche Framework wichtig ist:
  • TGameField
    Beschreibt das eigentliche Spielfeld

  • TPlayer
    Abstrakte klasse für einen Spieler. Diese Klasse wird für KI-Implementationen erweitert.

  • TGameRenderer
    Abstrakte klasse für den Renderer, also das Teil, was das Spielfeld zeichnet. Ich habe einen Simplen Beispielrenderer mitgegeben. Sollte sich wer berufen fühlen, kann er sich natürlich auch daran versuchen, da etwas schönes zu basteln Wink

  • TBullet
    Klasse für einen abgegebenen Schuss

SampleRenderer.bmx enthält die oben erwähnte Beispielimplementation für den Renderer.
SampleKI.bmx enthält die oben erwähnte Beispielimplementation für eine einfache KI und auch die Player-Klasse für den Menschlichen Spieler (Steuerung per Tastatur)
Start.bmx setzt alle Teile zu einer lauffähigen Demo zusammen Wink

Hinweise für eine Implementation: Da BlitzMax keine richtigen privates kennt, habe ich mich einfach auf eine eigene Konvention festgelegt: "Private" felder und methoden beginnen mit einem unterstrich. Solche dürfen in der implementation nicht verwendet werden. Alles andere ist erlaubt. (Ich hoffe, ich habe alles abgesichert, dass ein mogeln auf diese Weise nicht möglich sein sollte. Wenn doch, lasst es mich wissen)
Eine neue KI erstellt ihr, in dem ihr TPlayer erweitert und die update-methode überschreibt. Diese Methode stellt sozusagen einen einzelnen Spielzug dar.

Folgende Methoden könnt ihr in eurer KI verwenden:
turn(int), um sich zu drehen. Man kann der funktion sowohl positive und negative werte übergeben. Es gibt allerdings eine maximale Drehgeschwindigkeit, wenn diese überschritten wird, dreht sich die figur mit der maximal möglichen Geschwindigkeit in diese richtung
speedUp(), um die Geschwindigkeit zu erhöhen. Wenn die maximal mögliche Geschwindigkeit erreicht wird, passiert nichts
speedDown(), um die Geschwindigkeit zu drosseln. Die Figur kann sich mit einer langsamen Geschwindigkeit auch Rückwärts bewegen, was ebenfalls mit speedDown() erreicht wird.
break(), um zu bremsen. Das bedeutet einfach, dass, wenn die geschwindigkeit positiv ist, speedDown() aufgerufen wird, ansonsten speedUp(). Ausserdem stellt die funktion sicher, dass, wenn die geschwindigkeit nahe 0 ist, diese wirklich auf 0 gesetzt wird, anstatt in die andere Richtung zu gehen.
shoot(), um zu Schiessen. Je schneller man sich gerade bewegt, desto schneller fliegt auch die abgefeuerte Kugel.
getEnemys() liefert eine Liste mit allen Spielern auf dem Spielfeld, ausgenommen dem eigenen
getBullets() liefert eine Liste mit allen Kugeln, die gerade unterwegs sind.

Mein TODO:
- Skillpunkte-System. Es wird verschiedene Attribute geben (Geschwindigkeit, Wendigkeit, Beschleunigung, Kugelgeschwindigkeit, Kugelstärke und Energie fallen mir gerade Spontan ein), vor dem Spielbeginn wählt die KI die Verteilung ihrer Punkte.
- Teamspiel

So, das wars zur zeit. Für Fragen und Verbesserungsvorschläge bin ich natürlich offen.
Ich werd die Gelegenheit wohl nutzen und hier gleich mein erstes Worklog anlegen.

Grüße,
Smily
  • Zuletzt bearbeitet von Smily am Mi, Jan 13, 2010 11:05, insgesamt 3-mal bearbeitet

Firstdeathmaker

BeitragDi, Jan 12, 2010 11:09
Antworten mit Zitat
Benutzer-Profile anzeigen
Respawn wäre cool, dann könnte man seine KI auch lernen lassen Wink
www.illusion-games.de
Space War 3 | Space Race | Galaxy on Fire | Razoon
Gewinner des BCC #57 User posted image

Smily

BeitragDi, Jan 12, 2010 13:36
Antworten mit Zitat
Benutzer-Profile anzeigen
gute idee.
Man könnte auch erstmal die konstante PLAYER_HEALTH hochsetzen, dann kann ein Spieler mehr treffer einstecken und hat so mehr Zeit zum Lernen Wink

grüße,
Smily
Lesestoff:
gegen Softwarepatente | Netzzensur | brain.exe | Unabhängigkeitserklärung des Internets

"Wir müssen die Rechte der Andersdenkenden selbst dann beachten, wenn sie Idioten oder schädlich sind. Wir müssen aufpassen. Wachsamkeit ist der Preis der Freiheit --- Keine Zensur!"
stummi.org

Neue Antwort erstellen


Übersicht BlitzMax, BlitzMax NG Codearchiv & Module

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group