2011年11月20日星期日

简易Roguelike制作流程【转/待译】

原文地址:roguebasin.roguelikedevelopment.org



How to Write a Roguelike in 15 Steps


This is a rough route map for the creators of roguelikes. I'm well aware that there are many possible routes and that no two journeys are the same -- this is a description of one of possibilities and maybe will help you to plan your own.
Please add your comments -- maybe we could make this article really helpful. What are your own routes?


Contents
1 Step 1 - Decide to write a game
2 Step 2 - Hello world!
3 Step 3 - It's a boy!
4 Step 4 - The map
5 Step 5 - Saving/Loading
6 Step 6 - It's alive! Alive!
7 Step 7 - Interaction
8 Step 8 - Data files
9 Step 9 - Items
10 Step 10 - Magic
11 Step 11 - Simple game
12 Step 12 - Levels
13 Step 13 - Experience
14 Step 14 - Citizens
15 Step 15 - Free at last


Step 1 - Decide to write a game

Most of you will have this step behind you already, but there are some hints about the direction of the first step. The best reason to start developing your own roguelike game is to create a game that you will enjoy playing yourself.
Don't start by asking around about the definition of roguelike game -- you don't need it. If the game you write is not considered roguelike by others, but it's still fun to play for you -- you succeeded. It's not like there's some kind of a contest to write a game meeting certain specifications.
Don't plan too much. Of course, if you want certain things in your game, you must write it so that there is room for them -- but don't even try to anticipate everything -- it's just impossible. When you write a design doc, you make a lot of decisions. Most of the decisions can't be made without performing some test first -- usually by writing small programs utilizing given feature. It's best to make the decisions when your project has already reached appropriate stage -- when you don't need to write an additional program, because your project already has everything you need.
It's no fun to just proceed according to plan -- leave some space for improvisation. Don't be afraid about making mistakes or implementing something in an inflexible way -- you can improve it when you need it -- most of the time it will be ok.


Step 2 - Hello world!

Prepare your programming environment. Choose your language and platform, choose appropriate compilers/interpreters, editor, version control, automated build, and other utilities. Set them up so that you're comfortable with them.
Decide on which libraries you're going to use -- it can change later, but it's usually a painful change. Don't think too much about portability -- it can be fixed later. Think about what you need and what you're comfortable with.
Decide on the language you want to write comments and object names in your code, as well as the language you want to be used in your game. It's strongly recommended to use English in your source code -- you can get more help this way from others.
Don't worry about i18n yet; translation is usually a very late step in the development process.
Write a simple 'Hello world!' program and test whether it works. Test your libraries, etc. -- you don't want any surprises.
Start coding.


Step 3 - It's a boy!

Start with screen output and keyboard input. Decide preemptively on your screen layout (easy, you can change it later) and make the routines that display map characters, status lines and messages.
Make the routine to read key-presses (no config files, no keys redefinition).
Make a '@ walking around the empty screen' demo. Play with it a little, clean things up, play some more, imagining the game is finished and you're playing it.
Make the message handling routines -- especially for the debugging messages -- they come in handy.


Step 4 - The map

Decide on your map structure. Don't over-generalize -- you can add things later. Make your (empty) map displayed on the screen. Scrolled if you need it. Add some elements to your map to see if they are displayed correctly (just hard-code them, you don't need a level generator yet).
Make your '@' appear on the map -- not as it's element at first, just as a cursor. Test the scrolling, maybe make a 'look' command.
Now turn '@' into creature. You still don't have time implemented, so the keyboard-reading routines will just move it around the map. Include the checks for walls in the movement code. Maybe add doors and open/close commands.


Step 5 - Saving/Loading

Read the map from a file instead of hard-coding it. You can play with it and test different configurations if you make it human-readable from the start.
Add the 'save' command and the procedures to save your game -- the map at first, then, gradually, all of other game elements. From now on, when you implement something that needs saving, also implement saving it as soon as possible.
Here's a handy article about save files and how to implement them; it's worthwhile to read when planning ahead what form you'll save things in.
Once you work with files, you can make your preferences and keybindings config files. Again, if you make them human-readable, you'll save yourself a lot of trouble.
Now, when you're not sure how any element of the game works, you can save the game to a file and just check it.


Step 6 - It's alive! Alive!

Implement other creatures (monsters) and time. Add a single monster to begin with. Give him some simple AI (like, say, stay still, or maybe move randomly).
Start with my turn-your turn, then implement the time system you want (or, usually, a simplification of it and gradually make it more complicated later).
Remember to test everything as you go.


Step 7 - Interaction

Add stats for your creatures. A simplification of the ones you envisioned, probably. It's best to add stats as they are needed, not because they 'look cool', but you might not be able to resist ;).
Make the creatures notice each other -- bump, attack, etc. Gradually improve their AIs, so that they can chase the player.
Implement and test the combat system -- without equipment for now, just hardcode the values. Do lots of testing.


Step 8 - Data files

Move the creature, map features, etc. definitions to data files. Forget about scripting for now If something cannot be moved -- just leave it for now.


Step 9 - Items

Add items. For start, just objects you can pick up -- no properties. Gradually give them properties, kinds, stats, etc., implement inventory, picking up and dropping, equipping and using (no effects yet), also stacking, containers (if you want them), etc.
This step usually requires lots of testing to balance.


Step 10 - Magic

Add item effects, special monster attacks, spells. Add items and monsters to test them. You don't need all the possible effects right away, just the ones that are needed for the next step.


Step 11 - Simple game

Try to make a simple, hard-coded game. Play it and give it to your friends. Test the mechanics you've implemented so far. See if the game is fun.
Change everything you need to change. Don't forget to test a lot. Always ask someone to test the game's 'fun factor', or test it yourself after a while; it's hard to notice some things right away.
This step should take a pretty long time, until you've got a playable, fun mini-game.


Step 12 - Levels

Write your level generators. Implement moving between the levels, a wilderness and/or town if you want them, saving the levels on level change if you want them to be persistent (so that they don't take up memory).
Spread your monsters and items on different depths. Add more monsters and items, with their effects, as needed.


Step 13 - Experience

Start developing your experience system and skill system. Tune the monsters' and items' stats. Make character generation screen, classes, races, and whatever else you need.
Playtest your game frequently.


Step 14 - Citizens

Add NPCs, shopkeepers, and simple quests if you need them. Keep adding features and tuning the stats.


Step 15 - Free at last

Start adding and testing all the 'unique' features you thought were so cool months (years?) ago, when you started the whole thing. Revise your opinions of them and see if they fit the game.
Write your pet random plot generator, factions system, infinite wilderness generator, neural network AI, or other unique feature, since you can now test it in a working game.


Original version submitted to rec.games.roguelike.development by Radomir 'The Sheep' Dopieralski

算24【备份】

以前写过,不过这次应该代码好看些了吧。
#!python2
from itertools import *
from operator import *
calc24 = lambda a,b,c,d:list(set(sum((
(o(p(i,q(j,k)),l)==24 and ["(%s%s(%s%s%s))%s%s"%s] or [])+
(o(i,p(q(j,k),l))==24 and ["%s%s((%s%s%s)%s%s)"%s] or [])+
(o(i,p(j,q(k,l)))==24 and ["%s%s(%s%s(%s%s%s))"%s] or [])+
(o(p(i,j),q(k,l))==24 and ["(%s%s%s)%s(%s%s%s)"%s] or [])+
(o(p(q(i,j),k),l)==24 and ["(((%s%s%s)%s%s)%s%s)"%s] or [])
for nums,opp in [((a,b,c,d),(add,sub,mul,lambda x,y:truediv(x,y) if y else float('nan')))]
for j,i,k,l in permutations(nums) for o,p,q in product(opp,repeat=3)
for r in [dict(zip(opp,("+","-","*","/")))] for s in [(i,r[o],j,r[p],k,r[q],l)]),[])))

>>> calc24(6,6,6,6)
['6+((6+6)+6)', '(((6+6)+6)+6)', '(6+(6+6))+6', '(6+6)+(6+6)', '(((6-6)-6)*6)', '(6-6)*(6+6)', '6+(6+(6+6))']
>>> calc24(2,3,8,9)
['(8/(9*3))-2', '8/(2/(9-3))', '8*(9-(2*3))', '8*((9/3)-2)', '(((9*3)/2)-8)', '(8*2)/(9-3)', '(((9/3)*8)-2)', '(9/3)-(2/8)', '(9*3)-(8/2)', '8*(9-(3*2))', '(9*(3-2))*8', '(9*(2-3))*8']

下面这个考虑了交换律的重复。故意写个很累赘的样子,
而且为了吻合IEEE还把mapcan和format等等都替换了一下。
(define (calc24 a b c d)
  (define (flat-map p x)
    (apply append (map p x)))
  (define (filter p x)
    (cond ((null? x) '())
          ((p (car x)) (cons (car x) (filter p (cdr x))))
          (else (filter p (cdr x)))))
  (define (drop-one x lst)
    (cond ((null? lst) '())
          ((equal? x (car lst)) (cdr lst))
          (else (cons (car lst) (drop-one x (cdr lst))))))
  (define (perm lst)
    (if (null? lst)
        '(())
        (flat-map
         (lambda (x)
           (map (lambda (y) (cons x y))
                (perm (drop-one x lst))))
         lst)))
  (define (pow lst n)
    (if (zero? n) '(())
        (flat-map
         (lambda (x)
           (map (lambda (y) (cons x y))
                (pow lst (- n 1))))
         lst)))
  (define (buld-exp num ops)
    (apply
     (lambda (i j k l)
       (apply
        (lambda (o p q)
          (list
           `(,o (,p ,i ,j) (,q ,k ,l))
           `(,o (,p ,i (,q ,j ,k)) ,l)))
        ops))
     num))
  (define (eval-exp exp)
    (if (list? exp)
        (let ((arg (map eval-exp (cdr exp)))
              (/ (lambda (x y)
                   (if (zero? y) +nan.0 (/ x y)))))
          (case (car exp)
            ((+) (apply + arg))
            ((-) (apply - arg))
            ((*) (apply * arg))
            ((/) (apply / arg))
            ((--) (apply - (reverse arg)))
            ((//) (apply / (reverse arg)))
            (else (error "op?"))))
        exp))
  (define (show-exp exp)
    (define (bin-op-str op arg)
      (apply (lambda (x y)
               (string-append "(" x op y ")"))
             (map (lambda (x)
                    (if (number? x) (number->string x) x)) arg)))
    (if (list? exp)
        (let ((arg (map show-exp (cdr exp)))
              (/ (lambda (x y) (if (zero? y) +nan.0 (/ x y)))))
          (case (car exp)
            ((+) (bin-op-str "+" arg))
            ((-) (bin-op-str "-" arg))
            ((*) (bin-op-str "*" arg))
            ((/) (bin-op-str "/" arg))
            ((--) (bin-op-str "-" (reverse arg)))
            ((//) (bin-op-str "/" (reverse arg)))
            (else (error "op?"))))
        exp))
  (define (unique lst)
    (if (null? lst) '()
        (cons (car lst)
              (unique
               (filter (lambda (x)
                         (not (equal? x (car lst)))) lst)))))
  (unique
   (flat-map
    (lambda (x)
      (flat-map
       (lambda (y)
         (flat-map
          (lambda (z)
            (if (= 24 (eval-exp z))
                (list (show-exp z))
                '()))
          (buld-exp x y)))
       (pow '(+ - * / -- //) 3)))
    (perm (list a b c d)))))

(calc24 2 3 8 9)
("(((9-3)/2)*8)" "(8/(2/(9-3)))" "((8/2)*(9-3))" "((9-3)/(2/8))" "((9-3)*(8/2))" "((8*(9-3))/2)" "((9-(2*3))*8)" "((9-(3*2))*8)")
(calc24 6 6 6 6)
("((6+6)+(6+6))" "((6+(6+6))+6)" "((6*6)-(6+6))" "(((6*6)-6)-6)")

更多的不同语言写的可以看这个:
http://rosettacode.org/wiki/24_game/Solve

乱如备份下平时在npp里面用来插入行首空格的东西。
能用,不过貌似可能会干出多余的事情来,比如不仅仅是行首的情况。
另外使用前务必先用一下npp自带的转义& < > "的菜单命令。
editor.pyreplace(r"^ ", r"&nbsp;")
def s():editor.pysearch(r"&nbsp; " ,lambda x,y:rp())
def rp():editor.pyreplace(r"&nbsp; ", r"&nbsp;&nbsp;") and False or s()
s()


如果要变成算24的游戏的话,还要一个随机数生成,和表达式计算的东西。

2011年11月19日星期六

存活确认

觉得blogger最近登录不便,所以这段时间有点当长文回收的地方在用了。暂且不说有多少内容,仅仅是那堆放腾起的尘埃,也够让自己呛一阵的。不过其实呢,既然这里作为一个自己感到自在的地方,也不必总太多估计。可以有一些过程中的,不成熟的,零时的,盲目的,白痴的想法也写出来。因为作为结果而言,至少是作为一个暂且还达不到的,或者没有做到的事情来说,其中的过程也是不可欠缺的。缺少了或者说完全的抛弃了愚昧的状态,却反而回避很多需要的或者说感到满足或欣喜的结果的话,失去和得到的就不是同一事物的不能层面,而是皆至于无的状态了。所以,在这里,还是来发帖确认一下成活吧。以前烦了很多傻了,以后还是会继续来犯傻,同时再多展示一些令人欣慰的事情吧。