Angband.oook.cz
Angband.oook.cz
AboutVariantsLadderForumCompetitionComicScreenshotsFunniesLinks

Go Back   Angband Forums > The real world > Idle chatter

Reply
 
Thread Tools Display Modes
Old March 29, 2012, 06:40   #21
AnonymousHero
Veteran
 
AnonymousHero's Avatar
 
Join Date: Jun 2007
Posts: 1,367
AnonymousHero is on a distinguished road
Quote:
Originally Posted by Derakon View Post
I have to admit I haven't done much work with immutable objects being central to a large-scale project. I'm not certain what you mean by "pushing state mutation to the edge of the program"; care to explicate?
Here's a more in-depth response:

Let's start by imagining the whole game state as mutable. That is, monster (instances) are mutable, player object is mutable, items are mutable. In addition the list of monsters on a level is mutable, the list of items on a level is mutable, etc. (So Thing and Set are both mutable.). Further, the entire game state (which points to the list of monsters, the list of items, etc. etc.) is itself mutable.

The idea then is to start by making the low-level bits immutable. This means that you can hand any function a "monster" value/reference or an "item" value/reference and not have to worry whether it's actually mutated or not, because it can never be. You'll know if the function can "change" the monster because it'll have to return a new monster instance instead of changing it in-place. This is the lowest level.

Now we move the same idea up a level into the collections of things (Set) -- you never actually modify the list of monsters directly (you can't, it's immutable), you just return a modified "copy" of the list with your modifications applied. (Don't worry this can be done efficiently, see "persistent collections".)

... and so on until we have the entire game state as being immutable. That is, if a function receives a reference to the whole game state then it can only "modify" bits that it returns. This is usually where it's a good idea to stop, so the reference to the game state is mutable, but all the referred to data is not.

In this view, the "main loop" essentially becomes
Code:
    state_ref = tick(state_ref)
(That is, you simply replace the old state reference with a reference to the new state. This is also efficient because you don't have to clone -- you can simply reuse references to whatever old state wasn't modified.)

I hope that makes the idea a little clearer.
AnonymousHero is offline   Reply With Quote
Old March 29, 2012, 17:48   #22
Derakon
Prophet
 
Derakon's Avatar
 
Join Date: Dec 2009
Posts: 8,828
Derakon is on a distinguished road
I see, thanks. It would seem like you'd have to use some nonstandard types to achieve this (the aforementioned "persistent collections"), but I can see the value at least at the lower level of immutable players and monsters and so on. Perhaps I'm missing something though, but it seems like if you wanted the entire game state to be immutable in this way then you'd have to do a lot of "destroying" the entire game state to replace it with a new one. Of course you aren't actually doing anything like that behind the scenes but I imagine that it'd make the code a bit messy. I mean, how do you avoid this?
Code:
gameState = gameState.updatePlayer(player.wield(newItem))
gameState = gameState.updateMonster(monster, player.attack(monster))
In other words, as the game state contains the entirety of the game, it seems like you'd have to create a "new" one every time any facet of it changes.
Derakon is offline   Reply With Quote
Old March 30, 2012, 11:44   #23
AnonymousHero
Veteran
 
AnonymousHero's Avatar
 
Join Date: Jun 2007
Posts: 1,367
AnonymousHero is on a distinguished road
Quote:
Originally Posted by Derakon View Post
I mean, how do you avoid this?
Code:
gameState = gameState.updatePlayer(player.wield(newItem))
gameState = gameState.updateMonster(monster, player.attack(monster))
You don't avoid it -- you embrace it!

I realize it may look quite alien at first, but I find that in the long run being explicit about state updates (rather than "allowing" them anywhere) is a huge help rather than a hindrance.

Functional languages do tend to have slightly better support for this style of programming so that you can avoid a little of the verbosity. In a language like, say, Python or Java you might settle on a compromise where e.g. the "dungeon level" object is a structure containing mutable references to immutable "monsters", "items", etc collections. That'd probably be about the right level of immutability for those languages.

Anyway, this is getting pretty far off-topic, so I'll leave it there .
AnonymousHero is offline   Reply With Quote
Old April 8, 2012, 19:28   #24
Derakon
Prophet
 
Derakon's Avatar
 
Join Date: Dec 2009
Posts: 8,828
Derakon is on a distinguished road
Well, I guess this was inevitable. I've started implementing this. 954 lines of Python is enough to get me the following:

* Two freely-resizable windows, one showing a map and the other a scrollable message list (actually it's capturing stdout, so I can write to it with print statements).
* A very simple map generator that creates an 80x24 arena with two creatures (a wanderer and the player) and an item.
* The wanderer will move about at random, but won't move through walls or the player.
* The player can pick up the item, look at his inventory, equip the item, look at his equipment, and attack the monster.
* Attack damage is calculated differently based on the weapon(s) the player has equipped.



Obviously I haven't spent much time on the display code yet.

Some code snippets:

Code:
## IDs for "fundamental" containers that will recur elsewhere in the code.
(
## Must implement receiveAttack(self, alt)
ATTACKERS,
## Must implement update(self, gameMap)
UPDATERS,
## Must implement onCommand(self, gameMap, command)
LISTENERS,
## We just like to keep track of this guy.
PLAYERS,
## Must implement canMoveThrough(self, alt)
MOVERS,
## Must implement pickupItem(self, item), removeItem(self, item)
CARRIERS,
## Must implement pickup(self), drop(self), examine(self).
ITEMS,
## No required functions
WIELDABLES,
## Must implement getMeleeDamage(self)
ITEMS_MELEE) = range(9)
Code:
    ## Receive a command from the user and propagate it to our listeners.
    # Return a list of prompts that need to be resolved based on the command.
    # \param source The origination point of the command; needed for 
    # interactive commands (e.g. requiring a list to be displayed).
    def onCommand(self, source, command):
        result = []
        if container.LISTENERS in self.idToContainerMap:
            for thing in self.idToContainerMap[container.LISTENERS]:
                prompt = thing.onCommand(self, source, command)
                if prompt:
                    result.append(prompt)
        return result
Code:
    ## Try to move the given Thing from the first position to the second. 
    # If there are obstructions, return a Container holding them. Otherwise,
    # update where we store the Thing (and its 'pos' field).
    def moveMe(self, thing, source, target):
        x, y = target
        blockers = self.cells[x][y].filter(container.MOVERS)
        result = container.Container()
        for blocker in blockers:
            if not blocker.canMoveThrough(thing):
                result.subscribe(blocker)
        if not result:
            # It can move there, so move it.
            self.cells[x][y].subscribe(thing)
            self.cells[source[0]][source[1]].unsubscribe(thing)
            thing.pos = target
        return result
There's still plenty of not-so-great code in here; for example, drawing is currently atrociously inefficient. Plus there's a bunch of placeholder do-nothings or bad default behaviors that need to be filled in. And of course, there's no real content to speak of. Still, the concept is illustrated.

If you want to download the code, it's in a tarball here. It needs Python and wxPython installed to run (do "python pyrel.py" to run it) but should have no other dependencies.
Derakon is offline   Reply With Quote
Old April 19, 2012, 21:16   #25
RogerN
Swordsman
 
RogerN's Avatar
 
Join Date: Jul 2008
Posts: 307
RogerN is on a distinguished road
Quote:
Originally Posted by ekolis View Post
Didn't someone a year or two ago produce a reasonable facsimile of Angband written in C#? I think it was called "Cryptband"...
Cryptband was a fun project while it lasted (fun to write at least). It's Angband-ish but frankly ZAngband probably has more in common with vanilla than Cryptband does.

Unfortunately I consider Cryptband to be a failure in terms of design. Refactoring was going to be a lot of work and I ended up using messy hacks to save time. Frankly I succumbed to the temptation of borrowing too much code from Angband, when a proper OO port should have done things radically different.

Long story short... I don't recommend using that project as a starting point for anything else
RogerN is offline   Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
Roguelike Radio andrewdoull Idle chatter 47 November 8, 2014 21:57
Random theorycrafting: "Pyrl" Derakon Variants 5 December 29, 2011 15:59
Roguelike Phylogeny Zappa Vanilla 5 August 14, 2009 22:32
Rationale for new stat gain model? Pete Mack Vanilla 8 February 10, 2009 22:01
Roguelike development diary andrewdoull Variants 0 May 14, 2007 11:35


All times are GMT +1. The time now is 19:49.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.