Bay 12 Games Forum

Please login or register.

Login with username, password and session length
Advanced search  
Pages: 1 ... 44 45 [46] 47 48 ... 796

Author Topic: if self.isCoder(): post() #Programming Thread  (Read 903774 times)

Mego

  • Bay Watcher
  • [PREFSTRING:MADNESS]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #675 on: January 17, 2012, 08:49:50 pm »

The most basic criteria of a game doesn't involve having stats at all. RPGs are not the most basic games.

Max White

  • Bay Watcher
  • Still not hollowed!
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #676 on: January 17, 2012, 08:53:32 pm »

Hey Levi, if you don't mind, I'd be curious to see an example of how you have this organized.

Q: Do you yet know about inheritance?
Because if so, I can give you a run down, otherwise this might take a bit of explaining to not look like elvish tongue.

Aqizzar

  • Bay Watcher
  • There is no 'U'.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #677 on: January 17, 2012, 08:54:43 pm »

I'm beginning to understand inheritance, but actually seeing some code would probably explain it a lot better.  Feel free to expound though, I'm sure anything you have to say would be informative.

The most basic criteria of a game doesn't involve having stats at all. RPGs are not the most basic games.

Obviously not, but a "game" where all you do is move a marker around and cause other markers to disappear by cursoring at them isn't really a "game" because there's nothing being compared.  Once I add some element of random chance or mathematics, then I can let myself call it a "game", because that's the criteria I set for myself.
Logged
And here is where my beef pops up like a looming awkward boner.
Please amplify your relaxed states.
Quote from: PTTG??
The ancients built these quote pyramids to forever store vast quantities of rage.

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: if self.isCoder(): post() #Programming Thread
« Reply #678 on: January 17, 2012, 08:56:19 pm »

Crap... I can't link to the repo on my crap home server. I can do it from tortoisesvn, but not subclipse.
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

SolarShado

  • Bay Watcher
  • Psi-Blade => Your Back
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #679 on: January 17, 2012, 09:01:11 pm »

Pseudocode for how I assume Levi's code would work:
Code: [Select]
List<AI> ai = {new RunAwayAI(), new DiggerAI(), new WanderAI()};

foreach(AI x in ai) {
  if(x.canAct()) { x.act(); break; } // 'break' exits the foreach loop early
}

In english, loop through the AIs, ask each in turn if it can do anything. If it can, perform the action, otherwise keep asking.

In other news: inline object notation is awesome. (I've been doing a fair bit of JavaScript lately.)
Logged
Avid (rabid?) Linux user. Preferred flavor: Arch

nenjin

  • Bay Watcher
  • Inscrubtable Exhortations of the Soul
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #680 on: January 17, 2012, 09:11:53 pm »

Quote
I'm beginning to understand inheritance, but actually seeing some code would probably explain it a lot better.  Feel free to expound though, I'm sure anything you have to say would be informative.

I'll just quote briefly from my C++ tome.

Code: [Select]
(header stuff)

#include "point.h"

class Circle : public Point { // Circle Inherits from Point
   (stuff) }

Depending on what you set in public, private or protected parts of the superclass, you can then define what any class inherits from it. In this example they're building different shape definitions that inherit properties from the common superclass Point. (From point, to circle, to cylinder, each new class inheriting what it needs from the superclass and adding new properties that the next class will inherit.)

That's single inheritance, and its pretty straightforward. It's also possible for classes to override properties they inherited from a superclass. Multiple inheritance is where it gets funky, and when classes have classes as their members, you need to be very deliberate about arranging access for all of them.

In a game example you could create a generic Actor superclass with all the "action" methods you'd need for a standard actor object in your game, then a player Class and a monster Class could inherit from the Actor superclass, getting the methods and base properties you need them to have, while they're also adding or overriding methods and properties they need for their specific function.
« Last Edit: January 17, 2012, 09:43:04 pm by nenjin »
Logged
Cautivo del Milagro seamos, Penitente.
Quote from: Viktor Frankl
When we are no longer able to change a situation, we are challenged to change ourselves.
Quote from: Sindain
Its kinda silly to complain that a friendly NPC isn't a well designed boss fight.
Quote from: Eric Blank
How will I cheese now assholes?
Quote from: MrRoboto75
Always spaghetti, never forghetti

Levi

  • Bay Watcher
  • Is a fish.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #681 on: January 17, 2012, 09:16:58 pm »

Hey Levi, if you don't mind, I'd be curious to see an example of how you have this organized.

Sure, although its all in python so it'll likely be a little different.

So I've got a Monster class that looks vaguely like this:

Code: [Select]
class Monster:
def __init__(self, map):
self.speed = 10
self.x = 0
self.y = 0
self.fg = tcod.red
self.bg = tcod.yellow
self.char = '?'
self.redraw = True
self.map = map
self.path = None
self.dig_strength = 0
self.pathing = False
self.hero = False
self.max_hp = 5
self.hp = 5
self.melee = 0

def take_turn(self):
res = 'pass'
for ai in self.ai_list:
res = ai.take_turn()
if(res != 'pass'):
break
return res

Take turn is called in my main loop.  It goes through each AI Object in sequence and calls take_turn() on it.  My AI objects don't have a base class because I'm using duck typing.  As long as they provide the take_turn() method they work fine.  In c# you might have to create an interface or super class or something.

My actual monsters aren't derived with inheritance from the Monster class.  I actually build them up from a definition.

Here is an orc:
Code: [Select]
class OrcDef():
name = 'Orc'
def __init__(self):
pass

def build(self, mon):
mon.mon_type = OrcDef.name
mon.char = 'o'
mon.fg = tcod.Color(139,69,19)
mon.bg = None
mon.ai_list = [monsters.ai.AIFlee(mon), monsters.ai.AIFight(mon), monsters.ai.AIWander(mon)]
mon.dig_strength = 0
mon.max_hp = 20
mon.melee = 3

monsters.add_def(OrcDef())

At the end, it creates an instance of the OrcDef object and calls my add_def method, which just adds the orc to a list of spawnable monsters.  I've also got a spawn method that builds a monster object up based on the definition:

Code: [Select]
def spawn(name, x, y):
global map, spawnlist
for montype in spawnlist:
if name == montype.name:
mon = monster.Monster(map)
montype.build(mon)
mon.x, mon.y = x, y
mon.hp = mon.max_hp  #set his hp
map.monster_list.append(mon)
map.tiles[mon.x][mon.y].blocked = True

So when I call spawn('orc', 3, 6) it'll look up the definition list for an orc, create a monster object and pass that monster object to the build method.  The build method will create an orc however it sees fit. 

You can see it also puts three AI objects into the ai_list of the monster.  All my AI objects have a take_turn method.  I've put in my complete AIDigger code below, which is probably the most complete out of all my AI classes at the moment.

Code: [Select]
from util import *
import actions

import random
import libtcodpy as tcod

class AIDigger():
def __init__(self, monster):
self.monster = monster
self.path = monster.path
self.map = monster.map
pass

def take_turn(self):
mp = self.map
monster = self.monster

if mp.designations.get(repr([monster.x, monster.y - 1])) == 'D' and actions.dig(mp, monster, 'n'):
monster.pathing = False
return 'dug'
elif mp.designations.get(repr([monster.x, monster.y + 1])) == 'D' and actions.dig(mp, monster, 's'):
monster.pathing = False
return 'dug'
elif mp.designations.get(repr([monster.x - 1, monster.y])) == 'D' and actions.dig(mp, monster, 'w'):
monster.pathing = False
return 'dug'
elif mp.designations.get(repr([monster.x + 1, monster.y])) == 'D' and actions.dig(mp, monster, 'e'):
monster.pathing = False
return 'dug'

if not monster.pathing:
#look for digging spot
deslist = []
shortdeslist = []

for des in mp.dig_free_spots:
#1 check to see if its even next to open space
#x, y = map(lambda x: int(x), des[1:-1].split(', ')) #slow
x = int(des[1:-1].split(', ')[0])
y = int(des[1:-1].split(', ')[1])
if mp.dig_free_spots.get(des):
deslist.append([x,y])
if dist(x,y, monster.x, monster.y) <= 2:
shortdeslist.append([x,y])

if len(shortdeslist) > 0: #Get the close by ones first
random.shuffle(shortdeslist)
monster.pathing = tcod.path_compute(self.path, monster.x, monster.y, shortdeslist[0][0], shortdeslist[0][1])

if not monster.pathing and len(deslist) > 0:
#2 sort random
random.shuffle(deslist)
#3 pathfind each one until you find a valid path.
for d in deslist:
monster.pathing = tcod.path_compute(self.path, monster.x, monster.y, d[0], d[1])
if monster.pathing:
break

#walk to the designation like a proud warrior!
if monster.pathing:
x, y = tcod.path_walk(self.path, True)
if x == None:
monster.pathing = False
return 'pass'

if(x == monster.x + 1 and actions.walk(mp, monster, 'e')):
return 'walk'
elif(y == monster.y + 1 and actions.walk(mp, monster, 's')):
return 'walk'
elif(x == monster.x - 1 and actions.walk(mp, monster, 'w')):
return 'walk'
elif(y == monster.y - 1 and actions.walk(mp, monster, 'n')):
return 'walk'

return 'pass'

I have an action namespace that contains various simple actions that are common between monsters, like action.walk or action.dig.  The idea is that they completely resolve the action so the rest of my code doesn't have to worry about it.  They also return false if the action could not be done and true if the action was successful, so they work double time as a sanity check.

Code: [Select]
def walk(map, unit, dir):
newx = unit.x
newy = unit.y
if dir == 'n' and not map.tiles[newx][newy - 1].blocked:
newy -= 1
elif dir == 'e' and not map.tiles[newx + 1][newy].blocked:
newx += 1
elif dir == 's' and not map.tiles[newx][newy + 1].blocked:
newy += 1
elif dir == 'w' and not map.tiles[newx - 1][newy].blocked:
newx -= 1
else:
return False

#everything checks out, move!
map.redraw_list.append([unit.x, unit.y])
map.tiles[unit.x][unit.y].blocked = False
map.tiles[newx][newy].blocked = True

unit.x = newx
unit.y = newy

unit.redraw = True
return True

Um.  The north, east, south west probably isn't the best way of doing things though.  I don't quite like it and I might change it later.   :P

That is all I can think of about how I'm doing it at the moment.  Let me know if you have more questions.
« Last Edit: January 17, 2012, 09:18:49 pm by Levi »
Logged
Avid Gamer | Goldfish Enthusiast | Canadian | Professional Layabout

Mego

  • Bay Watcher
  • [PREFSTRING:MADNESS]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #682 on: January 17, 2012, 09:18:15 pm »

It's been a while since I've done multiple inheritance in C++. Would you mind reminding me how it works, nenjin?

Nadaka

  • Bay Watcher
    • View Profile
    • http://www.nadaka.us
Re: if self.isCoder(): post() #Programming Thread
« Reply #683 on: January 17, 2012, 09:19:04 pm »

Fuck eclipse. I need the correct version of JavaHL to match the version of my svn server. Eclipse won't let me install the correct version of JavaHL because I already have a newer version downloaded.
Logged
Take me out to the black, tell them I ain't comin' back...
I don't care cause I'm still free, you can't take the sky from me...

I turned myself into a monster, to fight against the monsters of the world.

Max White

  • Bay Watcher
  • Still not hollowed!
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #684 on: January 17, 2012, 09:32:21 pm »

I'm beginning to understand inheritance, but actually seeing some code would probably explain it a lot better.  Feel free to expound though, I'm sure anything you have to say would be informative.

Ok, well what we are looking at here is most often called state pattern, but ours is a little different to handle multiple states and then give priority to them. This will mean a little custom fitting from what is stock standard, but if you don't know state pattern to start with, it should be fine.

To start with, we need a class for AI. The thing is that you can never have an object that is just AI, you need things that are a specific sort of AI, such as FleeAI or AttackAI. This means our AI class must be 'abstract', or not a tangle thing that you can have one of. For example.
Spoiler (click to show/hide)
There, it is now an abstract class. You can not make one, so the following will not compile.
Spoiler (click to show/hide)

Instead, we need something to inherit from it. Starting with the Flee AI, this is how you inherit
Spoiler (click to show/hide)
Yep, just ':' and then what you want to extend. The following will compile.
Spoiler (click to show/hide)
Notice that even though it is a new 'FleeAI', it is stored as a type of 'AIType'? This is because sub classes can be treated like there super classes. So let's add a method onto FleeAI.
Spoiler (click to show/hide)
Now, if I invoke it with the following code, do you think it will compile?
Spoiler (click to show/hide)
The answer is no, it will not. This is because like I said, it is being treated like an AIType, and that has no definition for Update(). Your code is agnostic as to what sort of AIType you are using, so it can't just assume you can call Update(). Instead, let's move it over to the AIType class, and add some more functionality to the FleeAI.
Spoiler (click to show/hide)

There, that will compile, so what do you think the console output will be? Well this!
Spoiler (click to show/hide)
Yep, that is right, nothing. This is because it is treating it like an AIType, and so calling the Update() method inside AIType, the one that doesn't print anything. We need for the method to be polite and step aside should there be an override. To do that, first we need to add 'virtual' to the definition inside the super class, like so.
Spoiler (click to show/hide)
This will still not output anything. Although the method in the super class is willing to step aside, we need to tell the method in the base class that it needs to step up, so to speak. We do this with the 'override' keyword, as so

Spoiler (click to show/hide)

Now, finally our output will look like this.
Spoiler (click to show/hide)

Although this is a half decent start, we have a problem. Every type of AI should do something on Update(), but right now there is nothing forcing an AI to have a definition for Update(), sort of an issue. We can force subclasses to override methods with the 'abstract' keyword on the method at the superclass, like so
Spoiler (click to show/hide)
You will notice that I removed the body of the method (The stuff inside the {}) and added a semicolen at the end of the line. This is required. Still, right now your program will not compile if any types of AIType do not have a definition for Update();


Continues in part 2, in a second.

nenjin

  • Bay Watcher
  • Inscrubtable Exhortations of the Soul
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #685 on: January 17, 2012, 09:34:02 pm »

It's been a while since I've done multiple inheritance in C++. Would you mind reminding me how it works, nenjin?

I'll quote you some Deitel & Deitel code.

Code: [Select]
#ifndef BASE1_H
#define BASE1_H

class Base1{
public:
      Base1( int x ) { value = x; }
      int getData() const { return value; }
protected:                //accessible to dervied classes
      int value;           //inherited from derived class
};

#endiff

----

#ifndef BASE2_H
#define BASE2_H

class Base2 {
public:
       Base2( char c ) { letter = c; }
       char getData() const { return letter }
protected:        //accessible to derived classes
       char letter;     //inherited by derived class
};

#endif

---


#ifndef DERIVED_H
#define DERIVED_H

#include <iostream>

using std::osstream;

#include "base1.h"
#include "base2.h"

//multiple inheritances
class Derived : public Base1, public Base2 {
      friend ostream &operator<<( ostream &, const Derived & );   //you can kind of ignore this, this is them using arcane stuff

public:
   Derived( int, char, double );
   double getReal() const;

private:
   double real;       //derived classes' private data
};

#endif
« Last Edit: January 17, 2012, 09:37:22 pm by nenjin »
Logged
Cautivo del Milagro seamos, Penitente.
Quote from: Viktor Frankl
When we are no longer able to change a situation, we are challenged to change ourselves.
Quote from: Sindain
Its kinda silly to complain that a friendly NPC isn't a well designed boss fight.
Quote from: Eric Blank
How will I cheese now assholes?
Quote from: MrRoboto75
Always spaghetti, never forghetti

Aqizzar

  • Bay Watcher
  • There is no 'U'.
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #686 on: January 17, 2012, 09:50:13 pm »

Continues in part 2, in a second.

This is me, chowing on popcorn in anticipation.  I have absolutely no experience with any of these ideas, and I very much wish to learn more.  I feel like I'm eleven and my older brother is teaching me about girls.

Hopefully Part 2 includes the explanation on what happens if two different extensions have an Update() method, as I assume they can and will.
Logged
And here is where my beef pops up like a looming awkward boner.
Please amplify your relaxed states.
Quote from: PTTG??
The ancients built these quote pyramids to forever store vast quantities of rage.

Mego

  • Bay Watcher
  • [PREFSTRING:MADNESS]
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #687 on: January 17, 2012, 09:57:37 pm »

Thanks, nenjin, I already understand the concepts of multiple inheritance, but I don't quite recall how C++ solves the diamond problem, and nothing Google has spat out at me has done anything for me.

GlyphGryph

  • Bay Watcher
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #688 on: January 17, 2012, 10:10:56 pm »

Quote
Hopefully Part 2 includes the explanation on what happens if two different extensions have an Update() method, as I assume they can and will.
Of course. And what happens is that the method for the object you called executes, as its doing now, and if the result is different for different objects, and they've got compatible outputs, and so long as they inherit from the same parent, everything still works fine.
Logged

Max White

  • Bay Watcher
  • Still not hollowed!
    • View Profile
Re: if self.isCoder(): post() #Programming Thread
« Reply #689 on: January 17, 2012, 10:19:29 pm »

Part 2: Attack of the critters!
Well we have the most basic framework for our AI going, so let's whip up a critter for our AI to manipulate
Spoiler (click to show/hide)

As you can see, it can run, and it can fight. Fighting will deplete it's health, while running will slowly restore it. Now, every Critter has a list of AITypes, but in the same way every AIType needs a critter to manipulate! Let's add that now
Spoiler (click to show/hide)
You will notice that the critter is protected, not private. If it were private then subtypes of AIType, like FleeType, wouldn't have access to it, but with the 'protected' keyword AIType can access it's critter, and FleeAI can too, but nothing else, just like nature intended!
While we are here, we should add a constructor to our AIType. Yes, just because it can't be created doesn't mean it can't have a constructor!
Spoiler (click to show/hide)
Once again, protected, as nothing else needs to see it, but out subclasses must be able to see it. This is because to make an object with a constructor, we need to call it's constructor, and we are making AITypes, but we call this in a different way to how we normally do.

You remember the 'this' keyword? Well there is a similar one called 'base' that works a lot like 'this', but instead of looking at itself, it will look at its super class, and the first time we are going to use it is in calling it's bases constructor.
Spoiler (click to show/hide)
This time public. We want other things to be able to make this class. Now, you will notice no main body to the constructor. We don't need it to do anything, so leave it empty, it's fine, all the cool kids do it and leaves room to add stuff later on when things get more complex. Now let's beef up out FleeAI!
Spoiler (click to show/hide)
Now when it is called to update, it will figure out if it needs to flee, and then tell the critter if it is done thinking or not. Pretty snappy! How about for diversity, we add another type of AI, called FightAI?

Spoiler (click to show/hide)

As you can see, fighting isn't as smart as fleeing, but that is ok. It will take a low preference on our critter, meaning it will only fight if it has nothing else to do. We now have two fully functional AITypes, but our critter needs a bit of work. It needs to be able to update, and go through each of it's AITypes until one of the does something. It could also use a few instances of AITypes on in it's list.
Spoiler (click to show/hide)

There, we now have a critter class that will do as it's AI asks it to, so let's test it with this.
Spoiler (click to show/hide)

That will make a new critter, and make it run over 10 ticks. Run that and see what happens!
This is all a very simplified version of what will need to be more complex in a working roguelike, but you get the idea
-The critter runs over it's AITypes until one of them says to stop
-The AITypes all inherit a single class, and all override the same method
-In that method is where the thinking is done
-The AITypes get their information from the critter, decide what to do with it, and then command the critter


EDIT: Also, there is a part three, and with it, a level of power so great that you will feel static sparks come from your finger tips. Tell me if anybody feels good with this so far and wants me to write it up.
Pages: 1 ... 44 45 [46] 47 48 ... 796