Tuesday, February 5, 2008

Migration!

0 comments

I moved to http://www.sauron.fsdev.net/ Don't worry, I'm okay! Just go there if you're following an out-of-date link.

Wednesday, January 30, 2008

I Hate Specifications

0 comments

I got the pleasurable experience of writing a specification. General Brownlow was complaining to me that he hasn't been doing anything with relation to the game because he hasn't been given any specific tasks to complete. I found this rather humorous, since the last time I tried to convince him to do something he did something completely different. Then the time after than he ignored it and persisted in doing that different thing. Weird.

So he's incapable of drawing. Acutally, I don't really know whether he is or not, he just refuses to do it. I asked him for a storyboard for another project, you see, and he completely ignored it and write a small novel. I told him we don't have the time or resources to write a game off of a book. He said they did it for Halo. I told him that I will personally rape Halo and then proceed to violate its children.

He laughed.

So this time around I decided to get wise and write a task for him that's more suited to his quixotic habits. I asked him to write a game universe. Five of them, in fact.

Why do I want game universes? I want a variety so I can pick the best one! Well, actually I'm expecting them to find one pet universe and work almost exclusively on that. My hope then is for their guilty conscience to force them to think about the other universes they need to write drafts of so that they're thinking of new ideas to put in their pet universe.

The other part of my preference for a complete game universe draft before I'm code-complete on the first version of the game is to facilitate all those activities of game universe design before they try and put it in a game. Techmarine seems hell-bent on writing all this into Unreal Tournament, however, I'm reasonably confident he won't be able to make an RPG out of an FPS. I hope.

The other motive is for me to buy time to write the game. It's very difficult, requiring lots of careful forethought about how different systems need to be written to allow for easy modification at a later date.

I just hated writing that specification though. I bristled at having to use language such as "will consist of" and "shall have." It's just so much like a school assignment. If I don't use that language they'll read everything as a suggestion, and will completely ignore important things like concept art and the minutae of the game universe. The concept art is necessary to evolve into the artwork for the 2D mockup (the first version of the game, and the testbed for many of the mathematical and technological aspects of the game engine itself) and it's also going to be an absolute necessity in order to create a web page for the game as well. We can't stick with the Joomla! default theme. I know it's fully W3C valid, but that can't stop us!

Tuesday, January 29, 2008

Massive Economies

0 comments

Building a massively-multiplayer game does raise an interesting problem: how to craft a massive game that will also have a stable economy? It's almost ridiculous, since this is a problem big enough to require the hiring of a consultant from the financial world. That consultant would probably leave when he or she learned that we need their help to construct an imaginary economy.

So the game designer gets to add another dirty-job to the list: economist.

In other games this isn't quite as difficult a task. In a game like Might and Magic every kind of player asset (currency and items) come from a single source: the environment. The environment is really a term for the game. It could be drops from monsters, quest rewards, or NPC (non-player character) transactions. In a non-multiplayer game like Might and Magic, the old-school ones, this is permisable. There are no other players, thus it's just you and the game. Anything you loose is by definition put back into the environment.

What is not the environment? I define that to be anything that's owned by the player.

How do goods cycle back into the environment? As stated, through NPC transactions, through consumable items, and by either destroying or discarding items. In a multiplayer environment we also have a global economic standpoint, which comes to include inactive players and stuff lost when an account or character is deleted.

So ultimately it's like the Circle of Life from The Lion King. Everything comes from and goes back to the environment. In theory stuff also goes on inside the environment, however, that really isn't relevant since we can statistically simulate the wealth distribution inside the environment based on Saber-metrics (AI skill, character power ratings, proximity to other characters and how their ratings in specific areas compare, and other such things).

This is all well and dandy, however, there is another element that I want to discuss here. The goal of Kingdom Wars is to create a game that focuses also upon the interaction between players, and not just the playing of the game itself. This emphasis on human interaction should extend to the economy. This makes my goal simple: keep the money in the hands of players as long as possible before returning it to the environment.

By doing this I hope to make it more interesting, involving players with certain production skills they can develop to make money and then spend it by paying other players who have different skills for their services. This makes the game much more dynamic, with fluctuating prices that add that element of a gamble to the equation. Not only do you have to be a warrior, but now you have to think like a stock-market investor and buy low, sell high.

Biodude and I even entertained the concept of totally devaluing monster drops and completely removing NPC merchants altogether. This would force players to adopt a more frugal play style involving greater shrewdness. It also will cultivate greater levels of social networking, since friends usually help friends out.

Earlier on the FSDEV Forum I categorized the three different main types of MMORPG player. The first was a Grinder, who is a introverted individual who maintains few friends. Those friends are very good, however. The Grinder will rarely team up, prefering to go it alone. The next is the Economist, a player that spends more time working the game market than other players. The last kind is the Networker, a player that makes friends with everyone and is quick to both ask for help and give help to those friends. Networkers are always in groups of friends. They're really clique-ish, if you know what I mean.

The concept of making the game more about player production skills and less about "farming" the game environment through self-power is a direct aim to make the game more people-oriented. This reflects my goal to make the game more communication-oriented, and to elevate it as a place to meet friends that follow you outside the game and into other parts of the digital world. This contrasts greatly with the current game model where it's almost impossible to find friends that you met inside a game without using cues from those friends. You shouldn't have to comb the Internet for your buddies, they should be right there already!

Monday, January 28, 2008

Fixed the ArrayStack

0 comments

If fixed my poor little ArrayStack. It should work now.

module tango.util.collection.ArrayStack;

/**
* Standard implementation of a stack using an array as a data container.
* This stack automatically shrinks itself when it gets too big.
*
* @author Chris Miller
* @date 28 Jan 2008
*/
public class ArrayStack(K) {

// l_bound=the first element (the top of the stack)
// when the stack is empty, l_bound==data.length+1
private uint l_bound;

private K[] data;

/**
* The amount by which the array grows when it needs to in order to
* address more elements.
*/
private float grow_size=1.50f; // DO NOT CHANGE THIS WITHOUT
// LOOKING AT THE CONTRACT TOWARDS THE END OF THE FILE!!!

/**
* The ratio of array size to actual data size that the stack will
* resize to the number of elements times the grow_size to when this
* ratio is met.
*/
private float shrink_threshold=4.00f; // DO NOT CHANGE THIS WITHOUT
// LOOKING AT THE CONTRACT TOWARDS THE END OF THE FILE!!!

/**
* Creates a new ArrayStack with room for five elements.
*/
public this(){
this(5iu);
}

/**
* Creates a new ArrayStack with room for <tt>size</tt> elements.
*/
// see DMD 1.00 spec pg. 157, it's really cool!
public this(uint size)
in { assert(size!=0ui); }
body {
data=new K[size];
l_bound=size;
}

/**
* Pushes a new element <tt>e</tt> onto the top of the stack. It will
* also use this time to perform any array resizing that it may need.
* If it's giving you trouble due to constant resizing of the array,
* consider using <tt>fast_push</tt>, which doesn't check for memory
* use issues.
*/
public void push(K e) {
else if(data.length-l_bound<=data.length*shrink_threshold
&&!isEmpty()&&data.length>=5)
ensureCapacity((data.length-l_bound)*grow_size);
fast_push(e);
}

/**
* Does the same thing as <tt>push</tt>, only this does not check
* for memory use problems (a really big array with nothing in it).
*/
public void fast_push(K e) {
if(l_bound<=1ui)
ensureCapacity(data.length*grow_size);
data[--l_bound]=e;
}

/**
* Ensures that the array has enough room for <tt>capacity</tt> number
* of elements, including elements already in the array.
*
* <b>NOTE</b>: If you use this, consider using <tt>fast_push</tt> and
* <tt>fast_pop</tt> due to otherwise active auto-resizing features
* that will try and keep your memory usage down!
*/
// ensure the total capacity of the array, not just for new elements
public void ensureCapacity(uint capacity)
in { assert(capacity>=data.length-l_bound&&capacity!=0ui); }
body {
K[capacity] new_data;
new_data[new_data.length-(data.length-l_bound)..new_data.length]=
data[l_bound..data.length];
l_bound=new_data.length-(data.length-l_bound);
data=new_data;
}

/**
* Peeks at the element at the top of the stack without changing
* anything in the stack. Returns <tt>null</tt> if the stack is empty.
*/
public K peek() {
if(l_bound==data.length+1)
return null;
return data[l_bound];
}

/**
* Returns the element at the top of the stack and removes it. This
* version also checks for memory use. See also <tt>fast_pop</tt>.
*/
public K pop() {
if(data.length-l_bound<=data.length*shrink_threshold
&&!isEmpty()&&data.length>=5)
ensureCapacity((data.length-l_bound)*grow_size);
return fast_pop();
}

/**
* Essentially the same as <tt>pop</tt>, only different in that it
* does not perform memory checking to prevent runaway memory use.
* This makes it faster in certain situations because it doesn't
* constantly shrink the array.
*/
public K fast_pop() {
if(l_bound==data.length+1)
return null;
return data[l_bound++];

/**
* Returns whether the stack is empty or not.
*/
public boolean isEmpty() { return l_bound==data.length+1; }

/**
* Gets the growth size, or the number by which the current storage
* array size is multiplied by to find the new storage array size
* when more space is needed.
*/
public float getGrowSize() { return grow_size; }
/**
* Sets the growth size, or how much larger the new array size
* will be when the stack needs to make it bigger.
*/
public void setGrowSize(float grow_size)
in{ assert(grow_size>1.0f&&grow_size!=shrink_threshold); }
body { this.grow_size=grow_size; }
/**
* Gets the shrink threshold, or the ratio of array size to element
* count at which the stack will resize the array to be smaller.
*/
public float getShrinkThreshold() { return shrink_threshold; }
/**
* Sets the ratio at which the stack will shrink the container array to
* the number of elements times the grow size.
*/
public void setShrinkThreshold(float shrink_threshold)
in { assert(shrink_threshold>grow_size); }
body {
this.shrink_threshold=shrink_threshold;
}

}

Sunday, January 27, 2008

Coding under the Influence

0 comments

No, I wasn't drunk, my mind was just under the influence of being happy to code again. It's been really hard for me to spend so much time doing nothing but get ready to code.

This is another little anomaly which happened, though I'm not quite sure how it came to be.

module tango.util.collection.ArrayStack;

// written 20080122135351 PST
// by Chris Miller (lord Sauron the Great {at} gmail {dot} com)

/**
* Implementation of an stack data structure using an array. This
* implementation is auto-balancing and will shift itself to the front
* of its array to prevent runaway memory useage. It does this during
* its routine ensure capacity routine. It will also shrink its array
* size in the event that its internal array becomes inordinately larger
* than its current storage requirements.
*/
public class ArrayStack(K) {

// l_bound = left bound = the first element
// r_bound = right bound = the last element
uint l_bound=0, r_bound=0;

K[] data;

/**
* Default constructor; constructs a brand new ArrayStack just for you!
* It begins with room for five (5) elements. Obviously it can grow as
* needed.
*/
public ArrayStack() { this(5); }

/**
* Custom constructor; constructs a brand new ArrayStack just for you!
* It begins with room for uint size elements.
*/
public ArrayStack(uint size) {
assert(size!=0);
ensureCapacity(size);
} // public ArrayStack(uint size)

/**
* Utility to ensure that the ArrayStack has room for uint capacity
* number of elements. It will also perform a rebalance should it have
* enough space allocated but unavailable because it's stuck at the
* wrong end of the array.
*
* Please note that it is not a good idea to ensure a capactiy greater
* than four times the current because it will assume on the next
* push that it needs to shrink itself to prevent memory abuse.
*/
public void ensureCapacity(uint capacity) {
assert(capacity>=r_bound-l_bound);
if(data.length>=capacity) {
// shrink if we're grossly over-large
if(data.length>=capacity*4ui) {
// simply create a new array, let the garbage men
K[capacity] new_data=data[l_bound..r_bound];
data=new_data;
return;
// if the space left on the back of the stack and the
// space already taken in the array minus the null
// space on the front of the array is
// greater or equal to the desired capacity...
if(data.length-r_bound+(r_bound-l_bound)>=capacity)
return;
else rebalance();
} // if(data.length>=capacity)
K[capacity] new_data;
/* for(uint i=0; i!=r_bound-l_bound; i++)
new_data[i]=data[i+l_bound]; */
new_data[0..r_bound-l_bound] = data[l_bound..r_bound];
data=new_data;
r_bound-=l_bound; // make r_bound compensate for space at the
// beginning of the array that is no longer taken
l_bound=0;
} // public void ensureCapacity(uint capacity)

public K pop() {
if(r_bound-l_bound==0) // empty stack
return null;
return data[l_bound++];
} // public K pop()

public void rebalance() {
if(l_bound==0) return;
if(r_bound-l_bound==0) {
l_bound=0;
r_bound=0;
return;
} // if (r_bound-l_bound==0)
for(uint i=0; i!=r_bound-l_bound; i++)
data[i]=data[l_bound+i];
r_bound-=l_bound;
l_bound=0;
} // public void rebalance()

public K peek() {
if(r_bound-l_bound!=0)
return data[l_bound];
return null;
}

public void push(K e) {
if(data.length-r_bound==0)
ensureCapacity(data.length*1.5f);
data[r_bound++]=e;
} // public void push(K e)

} // class ArrayStack(K)


What's wrong? If you look closely it re balances itself to the front of the array. What's up with that? At the first push call it's going to have to move the whole dang data block over one. Actually, I think it might just do the dumb thing and shift it over zero places and then overwrite the first element. I only caught the error while reading over the code

I think I was really trying to create a queue, which is quite humorous to me. This is the second time I've tried to write one data structure and ended up writing something completely different. I really need to do this when I have more cylinders firing. But hey, at least it gives you some humorous code to read through. I tend to think it's really nice code, if only it'd work. I think I'll just throw the whole thing out and start anew. I know so much more about D now that I really think I could do a much better job with a fresh start.

Thursday, January 24, 2008

Additional Site Features

0 comments

Web applications suck. Interesting, since I'm typing into one right now. Just can't seem to break free from the Web 2.0 bubble. GMail, Blogger, Picasa... yet I have to roll my own development backend using a bunch of different things. It's just not fair.

I just put the Subversion repository browser back up. It's far better than it was a year ago when I first used it. I'm also moving the basic development toolkit list to the Forum. I don't think I'll put much into Joomla for a while longer, since no one seems to use it. It's good for super-global site news at least. Who knows, with the Joomla! 1.5 stuff in the community cooker who knows what cool tools will pop up? JFusion is one that's coming into its own, though it needs more work.

phpBB is nice, far better than Fireboard. I can't really blame Fireboard, however. I respect the heck out of both development teams. One just has a better product at the moment, that's all.

Why did I futz around with all these web things? Because no one has given me direction in the Tango project.

Nothing Comes in Black

1 comments

Man, nothing these days comes in black. I had to change themes. The code formatting just doesn't go well on a black background. I'm not going to sit there and fiddle with the colors! I know from my Digital Arts class that I cannot pick good colors to save my life. I can tell when colors do not work together, so I guess I'm okay in that respect. It just means I have to rely on more artistic people to pick them for me.

Computer Science Never Dies

0 comments

What am I doing right now? What I did last year in AP Computer Science: writing data structures. It's good for getting into D. Tango lacks any queues or stacks, so I figure I'll write 'em. Right now I'm having comprehensional altercations with the opApply function, which I think just filters elements during iteration and removes elements from the structure itself. I'm not quite sure though. It's spread over multiple files and interfaces and implemented very differently in different classes. Here's what I've got going so far:

module tango.util.collection.LinkedQueue;

private import tango.util.collection.impl.CLCell;

private import tango.util.collection.model.Queue,
tango.util.collection.model.Iterator;

/**
* Standard Queue implementation using a Linked List (circular doubly-linked).
*
* No iterator yet written, until such time please use while(!peek) { ... }
*
* @author Chris Miller
*/
public class LinkedQueue(K) : Queue!(K) {

/**
* Head node for the circular list.
*/
private CLCell!(K) head=null;

/**
* Creates a brand-new LinkedQueue, all prepped and ready just for you!
*/
public LinkedQueue();

/**
* Pushes an element onto the (back) of the queue (first in, first out).
*/
public void push(K e) {
if(!head) {
head=new CLCell(e);
return;
} // if(!head)
head.addPrev(e);
} // public void push(K e)

/**
* Peeks at the element that a call to pop() will return. Returns <b>null
* </b> if there are no more elements in the queue.
*/
public K peek() { if(!head) return null; return head.element(); }

/**
* Returns the element from the front of the queue and removes that element
* from the queue as well.
*/
public K pop() {
if(!head) return null;
else if(head.next() is head && head.prev() is head) {
K temp=head.element();
head=null;
return temp;
} // else if(head.left==head && head.right==head)
K temp=head.element();
head=head.next();
head.prev().unlink();
return temp;
} // public K pop()

private class LQueueIter : Iterator!(K) {
public bool more() {
return peek()!=null;
} // public bool more()
public K get() {
return pop();
}
int opApply(int delegate (inout V value) dg) {
return 0; // no idea what this does, I'll just leave it blank
}
} // private class LQueueIter : Iterator!(K)

unittest {
LinkedQueue!(int) test1=new LinkedQueue();
// add integers
for(int i=-50; i!=51; i++) {
test1.push(i);
} // for(int i=-50; i!=51; i++)
for(int i=-50; i!=51; i++) {
assert(test1.peek()==i);
assert(test1.peek()==test1.pop());
} // for(int i=-50; i!=51; i++)
} // unittest

} // class LinkedQueue(K)



How did I format the code for this blog? I spent a long time screwing around and then when Googling found this.

Sunday, January 20, 2008

Fresh Effort

0 comments

I wrote code tonight. It was exhilarating, almost like I was doing something I shouldn't be doing. It was too much fun to be legal. I haven't even gotten close to the good stuff, either. I just read some documentation and wrote a special singleton wrapper on a hash map for the purpose of globally storing references to SQLite database connections. It wasn't much at all, and I'm not even sure if it compiles yet, but it was fun.

I haven't done any real coding in a long while. I had a few false-starts in Java, and I think two more in C++. D on the other hand is being a lot of fun. I'm trying to isolate the why.

I think it's because I know that D performs on the same level as C++, which really does a lot to assuage my fears of running into a language-imposed technology barrier to the full potential of the game I'm writing. I'm a recovering Java programmer, and Java is so slow that it becomes completely necessary to make performance enhancements a kind of second nature. One of my biggest pet peeves in Java is String concatenation. Think of something like this:

String command_buffer = "";
while(magic_command_maker.hasMoreCommands())
    command_buffer += magic_command_maker.nextCommand();

I have personally run code profiles on stuff like that. A String in Java is an immutable type. It cannot be changed. The += operator just makes a new String the same length as the sum of the length of the two original Strings and then copies the character arrays of the two. That's slow. It's a memory allocation every time you add, and it's also setting you up for at least one block of memory to free later on, assuming that the part of the application that sent that String might still need it. That's a best-case scenario. Once I had a program that was spending literally 98.7% of its execution time in String concatenation. Crazy!

So I've established that I look for slow code before seeing it run. It's just a side effect of having known Java as well as I have. On the Allegro forums I think my signature still warns that I'm a recovering Java programmer.

Java isn't all bad, however. One thing I loved about Java was the API that worked everywhere and did all the mundane things for you. I didn't have to write and test large data structures outside of my computer science class. I never wrote my own GUI library. I have never tried to tokenize a String by hand. I really liked that toolbox that allowed me to focus on my job: writing the application.

C++ has a ton of libraries, and don't get me wrong, they're great. I think Boost C++ is awesome, and it's portable. However, I don't like C++'s concept of the header file. I think it really muddies the water with a ton of junk that really isn't relevant. I was finding that I understood C++ as a language exceptionally well, but I was having a lot of difficulty getting the environment set up so I could start writing. C++ seems to have a build tool for every library.

D isn't like that. In D it's a lot more like Java with the whole concept of a package or module and then the idea that a programmer imports that namespace into scope. It doesn't leave me grepping around for the file location and then re-verifying whether or not it's on the build path. In D, if it's not found, that means it's either not typed correctly or it's not on the build path to begin with. I find that to be a much more elegant solution and really simplifies the reason I'm there: to write software.

If you're wondering what brought on my happiness, if you refer back to revision two at the Kingdom Wars repository, you'll find the file db_handler.d. It's the best code I've gotten to write in a long time. That's a big problem for me. but I'm not going to let this fail this time. If I just think that I have critical mass, I will have critical mass. It's all a mind game.