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

2011年10月25日星期二

【怀旧向】关于ASCII风格Game的简单尝试 Part-I

堆了一个大体的框架,暂时没有实在的内容,还需要进一步抽象出可供不同数据复用的类型和模型出来。
目前是用一个修改版的TinyScheme做解释器,增加了平台实现相关的clrscr、inkey(=getch)、exit三个用于终端界面的函数。
为了以后移植方便,语言特性限定在IEEE和R5RS的最小子集的交集。
即无eval且call/cc只能局部跳出用,并只用到最基本的两种数字类型。

本来在别处写了一点说明和注释,价值不大就不搬过来了。
代码这里合并为单文件。
(define (require name)
;(push! *loaded*)
(load (string-append (symbol->string name) ".ss")))

;;;=====(load "core.ss")=====
(define (filter pred list)
(cond
((null? list) '())
((pred (car list))
(cons (car list) (filter pred (cdr list))))
(else (filter pred (cdr list)))))
(define (curry fun . args)
(lambda x
(apply fun (append args x))))
(define (assert ture . msgs)
(if (not ture) (begin (for-each (lambda (x) (display x)) msgs) (error))))

;;;=====(require 'util)=====
(define (wait-key)
(define (co key-map)
(define (co lst)
(cons
(char-downcase
(string-ref (symbol->string (car lst)) 0))
(cadr lst)))
(map co key-map))
(define key-map
(co '((w u)(s d)(a l)(d r)(z a)(x b)(c s))))
(let loop ()
(cond
((assoc (inkey) key-map)
=>(lambda (x) (if x (cdr x) (loop)))))))
;;;key-test
;(clrscr)
;(let loop ()
; (let ((c (inkey)))
; (display (char->integer c))
; (newline)
; (loop)))

;;;=====(require 'view)=====
(define (dispatch->callable host)
(define (object message . args)
(apply (host message) args))
object)

;;;choose:a modal scene
(define (ui-choose msg . items)
(define (show vec cur)
(define cur (modulo cur (vector-length vec)))
(clrscr)
(display msg)
(newline)
(do ((i 0 (+ i 1))) ((= i (vector-length vec)) 1)
(display (if (= i cur) " * " " "))
(display (vector-ref vec i))
(newline))
(let ((k (wait-key)))
(case k
((u) (show vec (- cur 1)))
((d) (show vec (+ cur 1)))
((a) (+ cur 1))
(else (show vec cur)))))
(show (list->vector items) 0))
;(display (ui-choose "where to go?" "walk" "eat" "sleep"))

;;;(int*int->())->(symbol->procedure)
(define (make-movable call-with-this-new-position)
(define position (list 0 0))
(define (position-ref)
position)
(define (position-copy)
(append position))
(define (position-set! x y)
(set-car! position x)
(set-car! (cdr position) y))
(define (move dx dy)
(let ((new-x (+ dx (car position)))
(new-y (+ dy (cadr position))))
;;tail-rec/cps
(call-with-this-new-position this new-x new-y)))
(define (dispatch message)
(case message
((position-ref) position-ref)
((position) position-copy)
((position-set!) position-set!)
((put) position-set!)
((move) move)
(else (error message))))
(define this (dispatch->callable dispatch))
this)
;;;ui-display-map
(define (map-view-display width height call-with-char-position)
(do ((y 0 (+ y 1))) ((= y height))
(do ((x 0 (+ x 1))) ((= x width))
(let ((chr (call-with-char-position x y)))
(if (or (not chr) (null? chr))
(display ".")
(display chr))))
(display "\n")))
;;;with-system-mune
(define (input-and-move move)
(case (wait-key)
((u) (move +0 -1))
((d) (move +0 +1))
((l) (move -1 +0))
((r) (move +1 +0))))
;;assoc-all
(define (assoc-all obj alist)
(let ((f (filter (lambda (x) (equal? (car x) obj)) alist)))
(if (null? f) #f f)))
;;make-pos-list
(define (pos-list-of-strs map-data)
'(demo-data
("##....###"
"#.$.....#"
"#.....@.#"
"#.$..####"))
(let loop ((x 0) (y 0) (w 0) (h 0)(it map-data) (ret '()))
(cond
((null? it)
(cons (list w h) ret));return this
((= x (string-length (car it)))
(loop 0 (+ y 1) (max x w) (+ h 1) (cdr it) ret))
((equal? (string-ref (car it) x) #\.)
(loop (+ x 1) y w h it ret))
(else
(loop (+ x 1) y w h it
(cons (list (list x y) (string-ref (car it) x)) ret))))))
;;;pos-list
(define (make-pos-list string-list-data)
(define _ (pos-list-of-strs string-list-data))
(define (dispatch message)
(case message
((width)
(lambda () (caar _)))
((height)
(lambda () (cadar _)))
((data-ref)
(lambda ()(cdr _)))
(else (error message))))
(dispatch->callable dispatch))
;;;=====(require 'view2)=====

;;;=====(require 'data)=====
(define (make-state)
(define alist-data (list))
(define (dispatch message)
(case message
((get)
(lambda (key)
(cond ((assoc key alist-data)=>(lambda (x) (cdr x)))(else #f))))
((put)
(lambda (key value)
(let ((ptr (assoc key alist-data)))
(if ptr (set-cdr! ptr value)
(set! alist-data (cons (cons key value) alist-data))))))
((save)
(lambda (file)
(call-with-output-file file
(lambda (port)
(write alist-data port)))))
((load)
(lambda (file)
(call-with-input-file file
(lambda (port)
(let ((sexp (read port)))
(if (eof-object? sexp)
(set! alist-data (list))
(set! alist-data sexp)))))))
(else (error message))))
dispatch)
;;;=====(require 'deprecate)=====

;;;=====(require 'main)=====
;;;make-map-modelA

;;;one map
(define (show-map-scene5)
(define (map-event p x y)
(cond
((assoc (list x y) (filter (lambda (x) (eqv? #\$ (cadr x))) map-objs))
(clrscr)
(display "hello world")
(inkey))
((assoc (list x y) map-objs)
=>(lambda (x) '()))
(else (p 'put x y)))
(map-loop))
(define p (make-movable map-event))
(define budr1 (make-pos-list
'("......................"
".......#.......#......"
"..........#......$...."
".....$................"
".............#........"
"......#..............."
"...............#......"
"........#.....#.......")))
(define map-objs
(cons
(list (p 'position-ref) "@")
(budr1 'data-ref)));map this
(define (map-char-of-pos x y);map-show make this simpler
(let ((lst (assoc-all (list x y) map-objs)));map-objs
(if (not lst) "." (cadr (car lst)))))
(define (map-loop)
(clrscr)
(display "[map]\n")
(map-view-display (budr1 'width) (budr1 'height) map-char-of-pos)
(input-and-move (curry p 'move)))
(map-loop))
(show-map-scene5)

(exit)


参考代码:
http://www.zincland.com/powder/
http://roguebasin.roguelikedevelopment.org
http://users.freebasic-portal.de/rdc/programs.html
http://marijnhaverbeke.nl/dunwich/
http://www.cs.cmu.edu/Groups/AI/lang/scheme/code/fun/advent.scm

等之后有空的时间来安排一个剧本来写完整吧,最近不会继续打理这个了。
上面说的东西是在自娱自乐,能够用来开心一下就目的达成。

2011年9月5日星期一

【挖一挖】关于文化娱乐方面的 Part-A

* 【挖一挖】
来写一些有印象的文化娱乐类的东西吧。

* Mario
偏向SFC的Super Mario World,玩法比之前丰富了,手感一如既往优秀。不同的关卡间有不同的主题和风格(话说有几个外传关卡变化得很牵强,但是SMW就感觉有亮点和贴切)顺便说下个人是非常不喜欢Zelda系列,每次攻击的时候感觉浑身不自在。

* FFTA
顺便把整个FF系列也在这里说了,首先抱怨下对FF战斗系统的不满,就是始终不适应(我知道100%逃跑是故意的)。另一个不满就是不停地跑存档点有时候会感觉到单调和无聊了,整个游戏玩法的亮点不断(对玩法和设定上亮点的期待和满足是件很有乐趣的感觉,不然就是重复刷关了)不过流程的整体感觉得有些分散了。估计是由于制作人的不同,系列中不同的风格有很强的辨识感的,所以这里在集中说下FF5和FF6吧。FF5的感觉是和伙伴一起在世界上闯关冒险的意思,一路上培养职业并利用能力的特性和属性来类似于动作游戏闯关一般去迷宫或城堡(路很简单,但是要注意敌人出现的种类和方式)里找Boss(FF向来100%逃跑率,并支持低等级去打Boss)。战斗是动态的,杀起血来也很快,如果所准备的战斗配备和战术安排不当的话,不会死撑太长时间(有时第一趟就是去送死的)。并且练级只是和副职业有关,主职业是可以自由转职的,这让战斗可以关注于当前的战斗本身。整个游戏过程被Boss划分为一个个区域,每个部分都是一个单独的关卡,强制流程使得战斗和情节都有节奏的进行着并在不同的关卡里面展示出不同的模式和玩法。有难度的同时,具有了乐趣和挑战性。不过FF5里面情节是串联作用了,紧凑有趣而不在于带入感什么的。至于FF6的战斗个人就觉得玩不来,而情节模式和表现方式方面则代表了随后的几款FF。FF6里情节是按照区域来展开的,每个区域里面有完整的流程,并有侧重的内容和一个大的目标。这样并不一定是走路Boss的模式了,而有可能有一些子任务或者阶段。跑路方面FF6比FF5麻烦一些,感觉像拖时间。由于情节是按照区域和类似于章节的形式来组织的,所以在表现的方式上相比之前要丰富了。战斗上面的亮点在于装备技能然后用熟练度记住技能,不过战斗本身我是觉得拼技能(这比等级重要)了,战斗上面每个职业(人物是固定职业)有不同的操作方式(不过其目的是代入感,但是对发挥战术有麻烦),有些地雷很拖慢节奏。情节上的一个亮点是刻画群像,利用人物间的经历相互补充、影响、团结,使得故事的展开显示出了立体感,并有了暗示核战争之类的意思。不过就是从比较开场和整个游戏过程,个人还是偏爱于FF5的叙事节奏和动作过关的感觉。至于后来的从FF7开始,有了CG动画,这是一种用镜头运用(焦点和位置)和场景调度构成的新的表达方式。特别是在FF8中丰富的镜头语言来增加画面的表现力。后来的FFX算是人物刻画和氛围渲染上塑造的更强烈的一作了,整个故事是一个旅途,但是哪怕是仅仅走路和不断穿插的强制时间,都在这种强烈的氛围之下变得有所含义。然后上面还有很强力的音乐没有说到,这个可以单独拿出来说,可惜这里不说了,总之就是很贴合和表现场景和人物。而且虽然上面说了好些牢骚,还是觉得FF的特性太显著和有亮点了,不同于大多数RPG侧重的冒险那一方面的感觉。
那啥标题是FFTA,话说那是因为觉得包括KH:CoM和FFCC的情节走上另一种套路,并且显得更为成熟一些。(例如FFTA是说主角和他们的伙伴们在生活中各有各不顺心的地方,然后在幻想世界里通过彼此的交锋,又以新的心态开始真正的生活。)在这几个游戏中,情节的表现是侧重于玩家知道什么和主角和其他角色们知道什么。情节是通过角色们的互动表现出来的,而不需要在这个进展的流程中让设定好的事件本身有如何的发展。角色被置于魔法科技的世界之中,让角色的身份、角色的经历、角色的性格成为了情节存在的方式,通过角色间信息和态度的发展和一个相当明显的强制人物线来推动情节的进行,和对某个或若干个关键词的表现里。让战斗成为交代人物关系和控制情节节凑的方式,在带入感方面比以往的FF要更加强烈一些,试图通过玩家控制角色的活动方式来影响玩家对整个情节的认识和情绪的渲染积蓄和迸发。主题方面侧重于了真实、记忆、梦想、愿望,这些属于个人方面的,而在先前的主题上如和平、友情、亲情、爱情、抗争、命运(还有水晶、多世界和故事年表中的环,以及女主必然公主,主角必然对其有好感)有所淡化。顺便说下FFT的几个相关作品的战斗模式很有亮点,注重节奏的同时,又控制住了节奏使得适合发挥策略。
说到水晶,FF1里有说,这是魔法的来源,FF6之后也会以魔石(前者是天然的,这个是合成的)的形态出现。在主题上,水晶代表目标、力量和欲望。
更多见www.ffsky.com那里的一些专题和讨论吧。

* Breath of Fire II
正好和FF互补,满足下个人喜好的另一面。章节式情节,每段有侧重的角色和地图。地雷用自动战斗,并注重地图机关的策略和Boss战的判断。亮点是路人NPC的对话也成为了交代情节的一部分,可以补充事件背景,可以来体现人物间的态度。整个故事情节和交予玩家的行动选择亦是用来体现此的。游戏中事件不是属于地图,而是某时刻在地图上发生,这样有了参与到人物任务事件的感觉,并且连续发生的一段情节也不会让事件点显得突兀,使得角色的行动不是为了触发情节而是成为了情节的一部分。

* 特鲁尼克大冒险
战斗很有亮点,系统也很丰富(魔法,地图属性,伙伴、宠物...),不多说了。顺便说下GBA上有几个战斗不错的游戏,战略类如鬼武者幽游白书公主同盟,还有几个即时控制多兵力一起玩的,还有在即使的行动中融入RPG的感觉的(例如思考数值上的行为寻则什么的)。

* Pokemon
战斗就是走过场,不过真的很有代入感。在那个时间里去各自认识了更多的人并获得了PM和徽章。

* 吾国吾民
林语堂的文字是很喜欢的,不过目前这个小说金华烟云还没看过,它是设定在一个立场摇摆的特殊年代。从一些短篇的散文中,一直可以觉得林的文字中对生活场景的描写充满着的情感的,十分具有杀伤力。说到小说的话,我这里有淡化情节而去关注到文字和写法的想法,因为情节可以仅看作故事设定的一个附属物。文字显得比鲁迅(写作入门用)要贴近读者些。《京华烟云》的电视剧渲染出来了面朝朦胧的未来去寻求的气氛。

* 五星饭店(?)
高中时候考到的电视剧,侦破、恋爱、成长多种要素被结合到了一起。不过让我印象最为深刻的是该电视剧的叙事方式,有一些段落着重关键的场景铺成大量的对话(类似于独白,因为是个人主导的)然后跳跃掉连接的行动的过程,也也一些段落没有对话完全依靠风景和镜头运动来体现时间轴的变动并体现出人物那时候的想法。情节是采取意外经历然后等级清零的处理,主题是关于真实与去做什么的事情,信息的细节的悬念穿梭全剧。

* 情书
是岩井俊二的小说和电影,这里说的是小说,平实的叙述推动着一幕幕的场景的发展。故事由藤井树的意外作为开端,以渡边博子和同名女生藤井树各种的生活和来往的信件作为发展,信件里回忆起了死去的藤井树的学生生活。在回想之中让一段逝去的经历变得清晰,变得真相和情感一直在那里,却一直没有旁人来察觉。人们在行动中探索者未来,同时也在明白着现在的由来。过去发生的事情并没有改变,而只是过去不可能一层事件的表象,当一个发生的事件阻隔在现在和过去的时候,一切的所为都被固定在一个有限的范围之中,去打开思绪外的一片世界。话说自己对看起来像是水面一样反射着表象却由于故事的发展充满了运动的态势的文字完全没有免疫力。

* 东方快车谋杀案
一直想能够看完阿加莎·克里斯蒂的全部作品,每个小说之间有着一致的写法但又每次阅读都能感到惊喜。小说的文本由对话和动作构成,并把内容按章节风格,文本的进行中透过所给的视角的信息不断的引导着读者的关注点,将故事以平稳的叙述化作眼前的一个舞台。读者透过这样的一个戏剧化(冲突和发展,发现真相的过程)的展示面,通过一个充满压力(凶杀案,已经发生的和将可能发生的)的时间和空间里面,去仔细的观察和打量每个人物。线索和行动在故事中进行着,人物间在互动同时也在展现着自身。有时候有动机的不止是一个人,他们都是整个故事背景中的一个视角,代表着一种可能性,是故事中的一个因素而只是没有往这方面发展。但是正是由于各种的可能性,使得这些小说有了更宽广的感觉。故事进行越往后,可能性在发生着收束,同时在放眼于远方的时候视线又忽视眼前灰色的角落,直到真相揭开视角的迷雾才最终散去。此刻恍然大悟,并将可能性组成一个清晰广阔的整体了。目前看过的印象中的还有《无人生还》《底牌》《怪屋》……

* 电视人·萤
论村上春树的国内出版物中,挪威的森林会较容易的接触到。不过这里是说的是两本短篇小说集,因为这里想要着重展示的其作品中把生活以异常的形态体现出真实的写法。这里举个有些距离的例子,比如轻小说文学少女中学姐经常吃书页有时会让主角写三题然后还吃出甘甜和苦涩来。吃书可以看出人物对文字的一种态度,可以看作是仅仅形容一种程度。但是故事不是把吃书这一行为停留在抽象的意象当中,而是具体地写,作为故事中发生的一件事性来写,当作了故事中理当发生的一件事情来写,以本来仅仅是意象层面的东西变成了一种存在着的真实。将意象具体化,并有由于意象来源于真实,当幻想的意象被放置于一个身边的世界中的时候,起初的突兀感便会转化为对其中真实成分的认同感。意象与真实成为了故事存在的一个支柱,与现代人的时代背景在一起,成为笼罩起整个小说的氛围。


* 警世通言
三言的第二本,明朝话本小说,故事题材各异,背景都是中国古代。小说的主题有点强加给人的意味,显得纸片化。不过确实是那时候的生活的展示,比较一些历史小说读起来觉得较贴近人些。并且每篇小说是以故事作为一个载体,传递着其中的主旨愿望和偏好。文字叙述也平易近人,写法上有现实有仙化的。

* 芥川龙之介
不得不说其短篇小说十分有魅力,大多都是十分压抑的,有让人喘气的压迫感。不要被《竹林中》迷惑了,以为是整个风格的代表。这篇仅仅是扑朔迷离,而不是更常见的对人物心态中的隐藏的一面的冲刷出色彩。虽然说灰色的文字常读不大好,不过他的每一篇小说风格各异的背景设定总能带给人创造力的感觉。故事并不是要发生什么事情,而是可以使得背景设定被展开为立体的存在。不论是真实或是神话,或者是发生来哪里,故事的背景都蕴含着那个世界中冲突的要素将要发展的一个全貌。

* Wikipedia
作为查阅资料的线索是十分有用的,有词条、词条的关联,以及词条所处的若然分类索引。可以用来建立对已有作品的整体概念,在构建设定的时候也可以作为查阅的资料和初始点。

* Lucky☆Star
目前自己觉得最耐看的一个漫画/动画,大段大段脑残(被指莫名其妙)的吐槽体现生活中发生过却没有注意到的细节。原作是关于学生的ACG生活的四格欢乐型漫画,每话内的话题是有连续关联的,并体现了不同人物的性格。

* 寒蝉鸣泣之时
暂且指TV版,游戏版还没动。先来说下类似的动画(后出)的原著(比寒蝉早)。《魍魉之匣》强调的是意外,案件本身没有复杂的手法,但是将同一个背景(二战之后)之下相干和不相干的事情串联在一起,显得扑朔迷离又意味深长。《尸鬼》给吸血鬼故事增加了人的犹豫,当吸血鬼依然能保留生者的思维的时候,它与人类双方的死活就存在态度情感和选择。然后话题回到寒蝉上,推理和多周目是亮点,解释着不同的可能性,同时也有着不会改变的和发生进步的一方面。对话是推动人物之间相了解相关心并团结的突破口,当人物的对话相互敞开的时候,正是胜利的期待放在面前的时候。

* 云之彼端,约束的地方
新海诚的作品中个人偏爱的一部,有不少第一人称的旁白,设定很抽象概括的感觉。情节方面前作星之声更能明显的体现出用设定来作为情感表达的依托这样的模式,不过这作显得更成熟些,作画和音乐都有明显的作者印记。

* 寂静岭
这里着重指1代以及电影版,顺便也指整个游戏系列的共同之处。故事的主题即事件的起因,这是故事最后揭开的内容,正是主角继续未来所需要正视的阴影。主角的经历过程,不仅是自己当前的,也是对往事的浮现和建立联系的过程,用探索场景来发现存在的故事。恐怖的来源有往事的幻象和外界宗教组织的压力。游戏玩起来有些累人,电影感觉代表了此类游戏的情节风格了。
同类型游戏零混合了现代都市和传统仪式,情节设定上觉得很有味道,情节展开有一步步回到历史上的某个关键时刻的感觉。

* 天堂电影院
长镜头很多,用回忆来美化回忆。有关于老者对主角成长的关注,关于Love的遗憾的悠长的曲调。

* Narcissu
某短篇GalGame,无选项可自动播放,其文字的情感与氛围相当显著。属于和别人在一个同时自己又弥散于两个人的空间里的那种感觉。同类型的有2和120元冬(其他几个季节的不喜欢)。文字有主角自己表达和故事情节发展的对话,看似随意的交融在一起,和声般在秋风里寻求着暖意。

* Kanon,Air
个人更偏向于Kanon,不过对着两者不是觉得喜欢,而是认可优秀。冬季和夏季的小镇给人的存在感十分强烈,情节中发生在不同地点背景图让人熟悉的这个地方,并了解了这里发生过的事情。形式上是好感度分支进人物线交代的人物的现状,然后在人物线上主角试图去了解人物帮助人物,最终解开了隐藏的心结。选项的设置使得玩家的操作和主角的情绪置于一致(开朗或内敛)然后真心去推人物线。泣系的风格让日常的情节有轻松和欢笑,而当回忆和现实两条线索交织在一起的时候,就释放了人物被能够表达出来的情感。

* School days
分支设定很神奇,包含两种情感偏向的曲折发展,内容以对话推动故事。用动画和限时选项(把未选算在内)来表现故事,发生着故事各种可能性。在主角的选择中,角色不是等待攻略,而是主动在情节中用行动和言语来影响主角的判断,让六话的情节在角色的互动之中推动着。这种三角形的恋爱故事,通过每一周目的玩家倾向(居然每个角色在情节安排上有引导主角改变倾向的主动行为),也算有带入感吧(像真实之泪那种也是故意纠结于感情方面的故事)。

* 动漫类
这个写起来就有些乱了,所以这里就不展开写了。鬼泣,豹头王,舞hime,妖精旋律...什么的就归类于此吧(有原作的这里不算在内)。

* South Park , The Simpsons , Futurama
美国动画中的代表,一话一个故事(偶尔有莫名其妙的话),情节紧凑展开让人关注。通常是根据现实中的事情来建立所表达的内容,把事件的争议置于公开来增进各方观点间的交流和改进。

* 逆转裁判 2
和地图事件型不同的是,体现出了与人物在言语上的对抗,并且有效的控制住了情节进展的节奏。有RPG的战斗感,道具和信息便是武器,在一个言语回合中判断着突破点。此外在地图事件中也体现出了时间的要素,使得每个角色在过程中都是不断行动。情节上说主线有一条人物的态度作为线索的,同时让回忆和未来清晰。GBA上的其他侦探类和它有模式上的差异,柯南侧重限时问答,One是行动决策,Q侦探是触发对话中的关键词。

* 妹妹公主
指两个TV动画以及GBA上游戏,以个发生的小故事来表达纯粹地想在一起的心愿,选项表示主角的行动和对角色的了解和态度。

* GalGame
算是一大类型,最近比较适合(节奏相协调的意思)这个,热衷于多周目全结局,以及选项和情节的配合。

* 补充
想到了一些(或许仅仅是碰巧想到了而已),然后就选择了一些来简介了一下。反映下个人的口味,顺便也算作是推广向文字吧。字数不多内容简略,且有自言自语不知所云的倾向。
话说很喜欢一系列小说和游戏中有相似和相异存在,在较平稳地对期待感的满足时有总是在期待有特别的东西出现。有时故事老套一些没有关系,但是要在意是如何把故事叙述和表现出来的,甚至利用设定来淡化情节线本身。

2011年8月28日星期日

【挖一挖】关于PE50的存档

* ProjectEuler

在projecteuler.net,很多小的程序题,可以用来练习键盘指法。限制比较宽松,只要不是很过分,暴力过关也没有关系。
虽说要尽量多用用数学知识来求解吧,不过拿出图灵完备的机器来辅助过关话,其实能算是很正当的方式呦。
高德納学派的可以用ASM,习惯传统方式可以用C++,天书般的J,函数式的Haskell,各种数学计算系统,或者偷懒用用Python,这些都是可以的做法。
既然我这里纯粹是出于娱乐目的的过关而已,就以Python2为主要工具了,也恰巧手头可以使用。

下面的内容算是流程的记录,这里粘贴在一个页面里而已。当初是正确的得到结果,并且性能可以说得过去,在这里就不再做什么改进了。


话说 Level 1 的内容实在十分基础了,没什么好说的了,下面闪入正文吧。


* Problem 1

print sum(i for i in xrange(1000) if (i%3==0) or (i%5==0))

这题的较好的做法是用等差数列求和,注意不把项记录重复了。


* Problem 2

(let loop ((s 0) (a 1) (b 2))
  (if (> b 4000000)
      s
      (loop (if (even? b) (+ s b) s) b (+ a b))))

这道题的优化方法是利用数列满足:奇偶奇奇偶奇奇偶奇...来计算。


* Problem 3

(let loop ((x 600851475143) (i 2))
  (if (> i (sqrt x))
      x
      (if (zero? (remainder x i))
          (loop (/ x i) i)
          (loop x (+ i 1)))))

偷懒可以用Maxima的factor函数,或者手写用正整数数列不断去除它,最后留下来的就是结果。


* Problem 4

(let loop ((r 0) (i 100) (j 100))
  (cond
    ((= i 1000) r)
    ((= j 1000) (loop r (+ i 1) (+ i 1)))
    (else
     (let* ((p (* i j))
            (s (string->list (number->string p))))
       (loop (if (and (equal? s (reverse s)) (> p r)) p r) i (+ j 1))))))

纯暴力,考虑了乘法交换积不变。不过这里回文判断实现得大不好,貌似可以推导出什么数学式子。


* Problem 5

(do((i 1 (+ i 1))
    (s 1 (lcm s i)))
  ((> i 20) s))

最小公倍数的算法貌似是欧几里德发明的。


* Problem 6

print sum(i for i in range(101))**2-sum(i**2 for i in range(101))

又暴力了,不过数列求和有那啥啥公式来着,不知道那些数学软件优化了没。


* Problem 7

(define (prime? x)
  (let loop ((i 2))
    (cond ((> (* i i) x) #t)
          ((zero? (remainder x i)) #f)
          (else (loop (+ 1 i))))))
(let loop ((i 2) (n 0))
  (if (prime? i)
      (if (= n 10000)
          i
          (loop (+ i 1) (+ n 1)))
      (loop (+ i 1) n)))

判断质数,后面会遇到更加丰富的用法的。


* Problem 8

import operator
s= ...
print max(reduce(operator.mul,map(int,s[i:i+5])) for i in xrange(len(s)-4))

又是暴力...题目字面的意思...不说什么了...


* Problem 9

a, b, c = 200, 375, 425

我才不告诉人家我是慢慢试出来的呢...


* Problem 10

m = 2000000
s = [True] * m
s[0] = False
s[1] = False
for i in xrange(2,m):
    if not s[i]:
        continue
    j = 2
    while True :
        k = i * j
        if k >= m:
            break
        s[k] = False
        j+=1
print sum(i for i,v in enumerate(s) if v)

传说中的划杠杠法,终于没有出现尾递归...


* Problem 11

from operator import mul
x = [
...
]
def s(r1,r2,d):
    return max(reduce(mul,[x[i+d[0]*n][j+d[1]*n] for n in range(4)]) for i in r1 for j in r2)
print max(
    s(xrange(0,20-3),xrange(0,20-0),(+1,+0)),
    s(xrange(0,20-0),xrange(0,20-3),(+0,+1)),
    s(xrange(0,20-3),xrange(0,20-3),(+1,+1)),
    s(xrange(0,20-3),xrange(0+3,20),(+1,-1)),
    )

字符串数据源直接拿正则表达式处理了,然后是幸好没差点把尝试的情况和范围弄错掉。


* Problem 12

(define (factor x)
  (let loop ((x x) (i 2) (n 1) (r '()))
    (if (> i (sqrt x))
        (cons x r)
        (if (zero? (remainder x i))
            (loop (/ x i) i (+ n 1) (cons i r))
            (loop x (+ i 1) n r)))))
(define (factor-exp x)
  (let ((x (factor x)))
    (let loop ((a (cdr x))(b '())(c (car x))(d 1))
      (if (null? a)
          (cons d b)
          (if (not (= (car a) c))
              (loop (cdr a) (cons d b) (car a) 1)
              (loop (cdr a) b c (+ d 1)))))))
(define (factor-num x)
  ;(assert (> x 1))
  (apply * (map (lambda (x) (+ x 1)) (factor-exp x))))
(let loop ((s 0)(i 1))
  (if (> (factor-num s) 500)
      s
      (loop (+ s i) (+ i 1))))

这题暴力的话效率实在不够,只好手疼地用排列组合的知识来优化一下了。


* Problem 13

x=[
...
]
print str(sum(x))[:10]

性能居然够,那我就不手疼了,不然从左往右算?根据100个n位数的和的位数肯定小于(n+3)来着...Java也有自带类似的库的说。


* Problem 14

m = 1000000
c = {}
c[1]=1
def n(x):
    if x in c:
        return c[x]
    if x%2==0:
        nx = x/2
    else:
        nx = 3*x+1
    v = n(nx)+1
    c[nx] = v
    return v
mx=0
mv=1
for i in xrange(1,m):
    t = n(i)
    if t>mx:
        mx, mv = t, i
print mv

代码写丑了我会直接说出来吗...这题超过边界不记忆的话估计性能也够了。


* Problem 15

load(functs);
combination(20+20,20);

这貌似是学排列组合的例题,那么就数学软件Maxima应对吧。
或者从左上角一步一步往右下角慢慢迭代推过去也是可以的。


* Problem 16

print sum(map(int,str(2**1000)))

依然高精度,性能没有问题就不优化了。


* Problem 17

(define (en n)
  ;(assert (<= 1 n 1000))
  (cond
    ((zero? n) "")
    ((= n 1000) "onethousand")
    ((zero? (remainder n 100))
     (string-append (en (quotient n 100)) "hundred"))
    ((< 100 n 1000)
     (string-append (en (quotient n 100)) "hundredand" (en (remainder n 100))))
    ((< 19 n 100)
     (string-append (vector-ref #("ten" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety") (- (quotient n 10) 1)) (en (remainder n 10))))
    ((< n 20)
     (vector-ref #("one" "two" "three" "four" "five" "six" "seven" "eight" "nine" "ten" "eleven" "twelve" "thirteen" "fourteen" "fifteen" "sixteen" "seventeen" "eighteen" "nineteen") (- n 1)))))
(do ((i 1 (+ i 1))(s 0 (+ s (string-length (en i)) )))((= i 1001) s))

我这里zero?的处理不大合代码的逻辑,只是在题目的范围内还不会影响到结果。不过这题其实扳手指算一个总数出来也就可以了。


* Problem 18

x = [
...
]
m = [[0]*i for i in range(1,len(x)+1)]
m[0][0]=x[0][0]
for i in range(len(x)-1):
    for j in range(i+1):
        print i, j
        t = m[i][j]+x[i+1][j]
        if m[i+1][j] < t:
            m[i+1][j] = t
        m[i+1][j+1] = m[i][j]+x[i+1][j+1]
print max(m[len(m)-1])

传统的DP题终于出现了呵,这里是手工用正则表达式获得数据后按照套路来处理。


* Problem 19

def getDaysOfMouth(yyyy,mm):
    _daysOfMouth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    def isLeap(y):
        return y%4==0 and y%400!=0
    return 29 if mm==1 and isLeap(yyyy) else _daysOfMouth[mm]
daysAfter = 0
daysOfSunday = 0
mod = sum(getDaysOfMouth(1990,m) for m in range(12)) % 7
for y in range(1901,2001):
    for m in range(12):
        if daysAfter%7==6 - mod :
            daysOfSunday += 1
        daysAfter += getDaysOfMouth(y,m)
print daysOfSunday

数字中用来好多魔法,千万小心别出Bug,这算法是代替手工操作的,需要小心和测试。话说一些语言的标准库里可是有Date和Calendar,而后者中能获取到星期相关的信息的。


* Problem 20

from math import factorial
print sum(map(int,str(factorial(100))))

高精度计算,拆位处可不涉及字符串,不过解释语言内置方法实现的比自己写的会效率好一些。


* Problem 21

c={}
def d(x):
    if x in c:
        return c[x]
    else:
        d = sum(i for i in xrange(1,x) if x%i==0)
        c[x] = d
        return d
s = 0
for i in range(1,10000):
    a = i
    b = d(a)
    if d(b)==a and a!=b:
        s += a
print s

前面某题做过求因数的优化了,这题暴力的话勉强够了。从实际命中的情况看,缓存用定长10000的数组对这题其实效率更好。


* Problem 22

f = open("names.txt","r")
l = f.readline()
c = l.split(",")
c.sort()
def s(x):
    return sum(ord(i)-ord('A')+1 for i in x[1:-1])
print sum(s(v)*i for i,v in enumerate(c,start=1))

第六行的里把引号过滤掉就目的达到,话说这次终于在IO方面没法省事直接内嵌数据了。


* Problem 23

m = 28123
def d(x):
    return sum(i for i in xrange(1,x) if x%i==0)
a = {}
for i in xrange(1,m):
    if d(i) > i:
        a[i] = True
def c(x):
    return any(True for i in xrange(1,x) if i in a and x-i in a)
print sum(i for i in xrange(1,m+1) if not c(i))

虽然结果对,但是像目前这样不优化的肯定超时。用Py2.7的特性可以把生产字典写为单行,可以省掉5~7th行。
另外用列表类型要注意Py的下标为负数也能取到元素,别弄错了。列表的话就写作“... a = [None]*m ... if a[i] and a[x-i] ... ”。


* Problem 24

from itertools import permutations,islice
print "".join(map(str,[i for i in islice(permutations(range(10)),999999,1000000)][0]))

某种意义的开挂,不过Py2的itertools代码明显不如Py3美观。优化的方法是位数从左到右计算可能的个数,排列的情况其实只用阶乘,然后手工调试出结果即可。


* Problem 25

(let loop ((a 1) (b 1) (n 1))
  (if (= (string-length (number->string a)) 1000)
      n
      (loop b (+ a b) (+ n 1))))

在Scheme里用尾递归表示迭代是相当好用的,有named-let和do两个宏可用。


* Problem 26

from itertools import count
def c(x):
    c = {}
    r = 1
    for i in count(0):
        t = r%x
        if t==0:
            return 0
        elif t in c:
            return i-c[r%x]
        else:
            c[t] = i
        r*=10
m, v = 0, 0
for i in range(1,1000):
    t = c(i)
    if t>v:
        m ,v = i ,t
print m

这里是模拟手工操作,不过貌似有数学推导式来着。


* Problem 27

from math import *
from itertools import *
c = {}
def p(x):
    if x in c:
        return c[x]
    else:
        p = (x==2 or
             (x>2 and x%2!=0 and
              all(x%i!=0 for i in xrange(3,int(sqrt(x))+1,2))))
        c[x] = p
        return p
def q(n,a,b):
    return n**2+a*n+b
m, v = (), 0
for a in xrange(-999,1000):
    for b in xrange(-999,1000):
        for x in count(0):
            if not p(q(x,a,b)):
                if x-1>v:
                    m,v=(a,b),x-1
                break
print m[0]*m[1]

怎么就没有带参数的max函数呢,不然派生一个Comparable类型出来更加累赘啊。


* Problem 28

size = 1001
def delta(x):
    return (x+2)**2-x**2
s,d = 1,1
for i in xrange(1,size,2):
    d += delta(i)
    s += d
print s*4-sum(range(2,size,2))*(1+2+3)-4+1

先算了右上角,然后扩展到四个角。其中用到的递推式(4~7th中的d)貌似可以化简。


* Problem 29

print len(set(a**b for a in xrange(2,101) for b in xrange(2,101)))

唉,又暴力了,居然性能还行,那就不用优化了。不用大数运算的话,其实重复项也只是底数呈指数关系的项。注意指数如果展开的话,不能用浮点数运算。


* Problem 30

from math import *
from operator import pow
from itertools import *
w = 0
for i in count(1):
    if 9**5*i < 10**i:
        w = i
        break
def t():
    for digits in product(range(10),repeat=w):
        m = reduce(lambda x,y:x*10+y,digits)
        if m!=0 and m!=1 and m==sum(i**5 for i in digits):
            yield m
print sum(t())

当位数达到一定大小后,和的最大值就已经满足不了位数了。由于 多项式函数==o(指数函数) ,从而可以求出全部解。


* Problem 31

from itertools import *
r = 200
p = [1,2,5,10,20,50,100,200]
n = 0
def t(s,pc):
    if pc>=len(p):
        return
    for i in count(0):
        l = s+i*p[pc]
        if l == r:
            global n
            n+=1
        if l >= r:
            return
        t(l,pc+1)
t(0,0)
print n

这次代码真的写丑了,不过运行效果良好。


* Problem 32

from itertools import *
digits2int = lambda ds: reduce(lambda h,l:10*h+l,ds)
def enum():
    for i in permutations(range(1,10)):
        for j in range(1,9):
            for k in range(j+1,9):
                if not (j-1)+(k-j-1)+1 <= k < (j-1)+(k-j-1)+3:
                    continue
                a,b,c = map(digits2int,(i[0:j],i[j:k],i[k:9]))
                if a*b==c:
                    yield c
print sum(set(enum()))

目前性能不行,如果把permutations展开写的话,可能可以免去一些方向的尝试。


* Problem 33

from functools import *
from itertools import *
from operator import *
from fractions import *
print apply(Fraction,reduce(partial(map,mul),((10*a+b,10*b+c) for a,b,c in product(range(1,10), repeat=3) if a<c and (10*a+b)*c==(10*b+c)*a))).denominator

好吧我承认这题很简单,但是我一开始愣是没明白题目要做什么。约分可以请欧几里德来算得最大公约数做除数,其他没什么好说的。


* Problem 34

from math import *
print sum(x for x in xrange(3,10**6) if sum(map(factorial,map(int,str(x))))==x)

以每一位都是9的情况和函数间的高阶关系来考虑上限,然后暴力。上限的位数是最大使得“factorial(9)*i>=10**i-1”成立的正整数。顺带说些Py带的若干函数是C实现的,性能比手工写要好很多。


* Problem 35

from math import *
from itertools import *
m = 10**6
s = [False,True] * (m/2)
s[0:3] = False, False, True
for i in xrange(3,m,2):
    if not s[i]:
        continue
    j = 2
    while True :
        k = i * j
        if k >= m:
            break
        s[k] = False
        j+=1
def primep(x):
    return s[x]
def rotations(x):
    s = str(x)
    return [s[i:len(s)]+s[0:i] for i in range(len(s))]
def cpp(x):
    return all(map(
        lambda t:primep(int("".join(t))),rotations(x)))
print len([True for i,v in enumerate(s) if v and cpp(i)])

一开始的时候每没意识到要优化,后来就好多了。有点等价代码的草稿,被我擦掉了。


* Problem 36

pp = lambda x:(lambda s:s==s[::-1])(str(x))
d2b = lambda x:"{:b}".format(x)
print sum(i for i in xrange(1,10**6) if pp(i) and pp(d2b(i)))

按照题目的意思来就可以了,没有遇到很糟糕的性能。


* Problem 37

from math import *
from itertools import *
from functools import *
m = 10**6
s = [False,True] * (m/2)
s[0:3] = False, False, True
for i in xrange(3,m,2):
    if not s[i]:
        continue
    j = 2
    while True :
        k = i * j
        if k >= m:
            break
        s[k] = False
        j+=1
primep = lambda x:s[x]
truncate = lambda x:(lambda s:
            imap(partial(map,int),
             ((s[:i+1],s[i:]) for i in range(len(s)))))(str(x))
tpp = lambda x:all(primep(a) and primep(b) for a,b in truncate(x))
t = [i for i in xrange(10,m) if tpp(i)]
print len(t)
print sum(t)

题目里说十一个,所以就算了这么一些,得到了answer。代码中用到了惰性求值,不然性能会很糟糕,避免了列表被完全展开。虽然没有编译时优化惰性求值有多余的开销,不过不必要的计算才是这里最影响性能的东西。


* Problem 38

from itertools import *
from functools import *
def t(x):
    "int->int"
    s = ""
    for i in count(start=1):
        s += str(x*i)
        if '0' in s:
            return 0
        if 9==len(s)==len(set(s)):
            return int(s)
        elif 9<=len(s):
            return 0
print max(t(reduce(lambda a,b:a*10+b,j,9))
       for i in range(0,4) for j in permutations(range(1,9),i))

需要尝试位数小于5的数,并注意没有零出现。


* Problem 39

print(reduce((lambda f,s:f if f[1]>s[1] else s),((m,len([a for a in xrange(1,m/3) for b in xrange(a,(m-a)/2) for c in (m-a-b,) if b-a<c<a+b and a**2+b**2==c**2])) for m in xrange(1001)))[0])

性能啊性能啊


* Problem 40

from operator import *
from itertools import *
from functools import *
print reduce(mul,(int(v) for i,v in islice(enumerate(chain.from_iterable(imap(str,count()))),1000001) if i in (1,10,100,1000,10000,100000,1000000)))

边迭代边判断即可。


* Problem 41

from math import *
from itertools import *
primep = lambda x:(x==2 or (x>2 and x%2!=0 and all(x%i!=0 for i in xrange(3,int(sqrt(x))+1,2))))
print max(k for i in range(1,10) for j in permutations(range(1,i+1),i) for k in (reduce(lambda x,y:10*x+y,j,0),) if primep(k))

就是题目字面的意思。


* Problem 42

c = (open("words.txt","r").readline()[1:-1]).split('","')
v = lambda x:sum(ord(i)-ord('A')+1 for i in x)
t = lambda n:(n*(n+1))/2
ts = [1]
def tp(x):
    while ts[-1]<x:
        ts.append(t(len(ts)+1))
    return x in ts
print len([i for i in c if tp(v(i))])

还有明显的优化空间,不过没感觉到性能瓶颈。


* Problem 43

from itertools import *
digits2int = lambda it:reduce(lambda a,b:10*a+b,it,0)
satisfy = lambda x:all(imap(lambda a,b:a%b==0,(digits2int(x[j:j+3]) for j in xrange(1,8)),(2,3,5,7,11,13,17)))
print sum(digits2int(i) for i in permutations(range(10)) if satisfy(i))

来多说说一些关于permutations函数的话题吧这里,因为最近用得较多了。话说STL里面也有同样作用的函数提供,而自己实现的话可以简单地利用树的遍历。


* Problem 44

from math import *
#from bisect import *
from itertools import *
p = lambda n:n*(3*n-1)/2
#ps = [p(1)]
#def pp(x):
# while ps[-1]<x:ps.append(p(len(ps)+1))
# return (lambda i:i<len(ps) and ps[i]==x)(bisect_left(ps,x))
pp = lambda x:((1+sqrt(1+24*x))/6)%1==0
print list(islice((p(i)-p(j) for i in count(1) for j in xrange(1,i) if (lambda x,y:pp(x+y) and pp(x-y))(p(i),p(j))),1))[0]

注释掉的代码是被重写了的,用了一元二次方程的求根公式(结合函数图形看,其实这里用错了,还没来及修改)。不用bin-search用hash-set貌似也行。


* Problem 45

(let loop ((i 286) (j 166) (k 144))
  (let ((t (* i (+ i 1) 1/2))
        (p (* j (- (* 3 j) 1) 1/2))
        (h (* k (- (* 2 k) 1))))
    (cond ((= t p h) t)
          ((<= t (min p h)) (loop (+ i 1) j k))
          ((<= p (min t h)) (loop i (+ j 1) k))
          ((<= h (min t p)) (loop i j (+ k 1)))
          (else #f))))
    
话说好久没用Scheme来写写了。


* Problem 46

from math import *
from itertools import *
oddp = lambda x:x%2!=0
primep = lambda x:(x==2 or (x>2 and x%2!=0 and all(x%i!=0 for i in xrange(3,int(sqrt(x))+1,2))))
conjecturep = lambda x:any(i for i in xrange(2,x) if primep(i) and sqrt((x-i)*.5)%1==0)
failp = lambda x:oddp(x) and not primep(x) and not conjecturep(x)
for i in count(4):
    if failp(i):
        print i
        break
#from functools import *
#from operator import *
#print list(islice(ifilter(failp,count(4)),1))[0]

直接算吧,然后直接答案取走。这里注释掉的是最后几行另一种运行很慢的写法。


* Problem 47

(define (factor x)
  (let loop ((x x) (i 2) (r '()))
    (if (> i (sqrt x))
        (cons x r)
        (if (zero? (remainder x i))
            (loop (/ x i) i (cons i r))
            (loop x (+ i 1) r)))))
(define (unique-sroted-list x)
  (define (list-not-starts-with xs x)
    (cond ((null? xs) '())
          ((eq? (car xs) x)
           (list-not-starts-with (cdr xs) x))
          (else xs)))
  (if (null? x) '()
      (cons (car x)
            (unique-sroted-list
             (list-not-starts-with (cdr x) (car x))))))
(define (number-of-distinct-primes x)
  (length (unique-sroted-list (factor x))))
(do ((n 1 (+ n 1))
     (a 0 (number-of-distinct-primes n))
     (b 0 (if (= a 4) (+ b 1) 0)))
  ((= b 4)(- n 4 2)))
  
现在越来越觉得Scheme代码想撮面团拧麻花的样子。理解清楚题意才是最重要的,然后运用对应的知识即可。


* Problem 48

last_ten_digits = lambda x:str(x) if x<10**11 else str(x)[-10:]
print last_ten_digits(sum(int(last_ten_digits(i**i)) for i in xrange(1,1001)))

求指数过程中只保留10位即可,可以获得更好的性能。以Python的性能还是算了,内置函数和手写落差太大,还是不修改了。


* Problem 49

from math import sqrt
print (lambda c:(lambda p:[''.join(map(str,(i,j,k))) for a in xrange(len(c)) for b in xrange(a+1,len(c)) for i,j,k in [(c[a],c[b],2*c[b]-c[a])] if k>j and k in p and sorted(str(i))==sorted(str(j))==sorted(str(k))])(set(c)))(filter(lambda x:(x==2 or (x>2 and x%2!=0 and all(x%i!=0 for i in xrange(3,int(sqrt(x))+1,2)))),range(1000,10000)))

把for语句压缩为单行代码后的产物,“import”那行不算数。


* Problem 50

m = 10**6
def prime(m):
    s = [False,True] * (m/2)
    s[0:3] = False, False, True
    for i in xrange(3,m,2):
        if not s[i]:
            continue
        j = 2
        while True :
            k = i * j
            if k >= m:
                break
            s[k] = False
            j+=1
    primep = lambda x:s[x]
    primes = tuple(i for i in xrange(m) if primep(i))
    return primep,primes
primep, primes = prime(m)
mr = 0
r = 0
for i in xrange(len(primes)):
    su = sum(primes[i:i+22])
    for j in xrange(i+22,len(primes)):
        lp = j-i+1
        su += primes[j]
        if su>=m:
            break
        if primep(su):
            if lp>mr:
                mr,r = lp,su
print r

一开始不专心弄复杂题目意思了,造成了不必要的浪费,本来是很简单的问题。这次未用函数式风格,话说控制不好状态的变化很容易出错的说。


* Level 1

前50题就是这些了,这是些算全部题目的 Level One 部分。
撒花,撒花。
接下来朝着别的目标前进吧,就先暂且做这几道题作为练手和娱乐了。

其实吧PE的前50道题目还是比较浅显的,因为涉及到的数据结构和数学上的知识点不是很多。
题目差不多都是根据题目的意思直接翻译为代码,把程序语言作为一个强大的计算器来使用。
所以在这前50道题里,更关注的是对机算工具的使用,而不是问题本身的经验积累。
那么其中有什么积极的价值呢,我想,或多或少总是有的吧,我只能这么说了。
上面的代码太偷懒了,不知道这种懒惰时候会让自己对正常向的使用态度有副作用呢。
好害怕...
其他的就不多说了,收工。

----
此外Problem 8试了试common lisp,不过代码没写漂亮,所以上面整理时被我略去了。
<blockquote>(defparameter s ...)
(reduce #'max (loop for i from 1 to (- (length s) 5) collect (reduce #'* (map 'list #'(lambda (x) (parse-integer (string x))) (subseq s i (+ i 5))))))</blockquote>

如果想临时试试代码的话可以用用codepad.org或者ideone.com。

----
最初在uushare发贴说一天一题,然后在QQ空间上整理出了上文,然后又把纯文字版备份于此(可惜格式图片都丢失了)。

2011年8月27日星期六

【挖一挖】WQX和单文件小游戏

作为可看作是集合了电子辞典,编程计算器,个人信息助理以及互动多媒体终端的这样一个东西,上面看起来还是有些有趣的东西的。还曾有一个写了一半的文字来记录以下从中能够想到的用法。

出发点是下面两个链接,然后来转一些片段:
http://www.emsky.net/bbs
http://javatar.iteye.com/category/7525


RPG引擎原理讲解! 作者:杨芹勍[Coolsoft(R)]
------------------------------------------------------------------------------------------------------
当我在发布“动物园历险记”之前,我在许多文曲星网站都发了游戏的剧情、内容和规模预告的帖子。可想而知,当你在没有玩这个游戏的时候肯定也不会相信GVBasic能处理那么复杂的剧情。和大家一样,那些回帖的人都说不可能有那么强的引擎能撑得住。但是当游戏发布时……这主要是归功于我花了一个星期才研究出来的“对象处理思想”。这个思想的用途是处理RPG剧情的前后关系。首先试用在《逃离校园》上,然后是《医院惊魂》,最后才与《动物园历险记》玩美地结合。如果想知道这个思想的具体函意,请往下看。
这个思想的灵感主要是来源于PC上许多编程软件的“面象对象”的思想。它可以把一个窗体、一个控件、一段代码等都看成是一个对象,每个对象都有自己的属性、事件和方法。一个对象可以激活另外一个对象,同时也可以终止一个对象。同样,文曲星屏幕上任何一个图形都可以看成是一个对象,这就是所谓的“对象处理思想”。当然我们并不是把屏幕上每个图形都看成是一个对象,而是处理一些和剧情有关的图形,那些图形所包含的信息就是我们要处理的剧情。我们可以把信息保存在数据文件里面,然后在主程序里面处理它们,这样就节省掉很多主程序的字节。
1、剧情信息格式
大家在地图安装文件“ZOOSC.BAS”里面是不是发现许多形如“ABTBCMBSA”这种奇怪的字符串?这就是地图上面的对象所包含的信息。下面让我们来详细分析一下这些信息所保存的格式:
首先,我们令A等于1,B等于2……Z等于26等等(用ASCII码表示就是CHR$(T+64),T=1,2,3……),为什么要那么做呢?是因为由于RPG包含的元素很多,如果这些元素大于或等于10的话处理起来会很麻烦,且保存起来很不方便。等一下你就会理解啦!
然后来看一下剧情信息包含的元素和保存的格式:
屏幕X坐标 屏幕Y坐标 条件 条件值 字幕 处理1 处理值1 处理2 处理值2
一共有9个元素,每个元素占位一个字节。
“屏幕Y坐标”就是屏幕上的1~5行,“X坐标”与我们平常习惯的不同,它算的是汉字的相对坐标,如果在屏幕上有一行字“一二三四”,那么“三”的实际X坐标是5,相对坐标是3。
“条件”就是你要实现这个剧情需要的条件识别符,如用“L”代表等级、“S”代表道具。“条件值”就是条件所对应的值,如多少等级、什么道具。如“SA”表示条件为必须要有第一个道具(“A”代表“1”!)剧情才能继续。
“字幕”就是你完成某个条件以后屏幕上弹出的对话字幕。通常RPG有很多人物对话,由于“字幕”这个元素只占一格,如果用1、2、3来表示那么只能有9条对话。那怎么行啊?所以我们可以用A、B、C~~代替。“~”的ASCII码值为126,那么从“A”~“~”中间有64个空位,比9大多啦!现在你可以理解为什么用这些东西代替了吧!
“处理”和“条件”大同小异,只不过“处理”是得到什么东西而不是条件那样需要什么东西罢了。
注意:在这些元素中不是每个元素都会用到,如果不用的元素请用“@”代替,因为“@”代替“0”。
下面让我们来看看一些常规的“条件”和“处理”分别对应的识别码。
条件:
H:有某道具;
L:达到多少等级;
T:几点钟;
Z:需要某特殊剧情;
N:不需要某特殊剧情。
处理:
C:跳转地图;
M:遇到某敌人;
S:得到某道具;
Z:处理某特殊剧情。
什么叫特殊剧情呢?特殊剧情就是指不改变玩家状态(道具、时间、等级),而只改变游戏进度的剧情。如在动物园历险记中,一开始有个小孩,你必须请教他才能钻狗洞进动物园。而你请教那个小孩,没有改变你任何状态,它只会给一个特殊变量附值,表明已经问过了。然后到狗洞前信息会引导程序去判断这个特殊变量的值,看看你是否问过小孩。如果小孩对应的处理信息为ZA,那么狗洞对应的条件信息为ZA。如果你想搞一个问过小孩就不能进狗洞,那么狗洞对应的条件信息应改为NA。
大家在玩动物园的时候肯定会有进某所房子或出某所房子的时候。这就是跳转语句起的作用。每一个跳转值都对应一个跳转字符串,这个等一下再说。
下面我们来进行举例分析:
在屏幕的第一行、第三格有一个图形,碰到它会遇到一号敌人,会出现第二个对话,打赢了得到第三个道具,怎么表示?很简单:CA@@BMASC。注意,由于没有条件,条件那两栏用@填充。
如果有某种道具时显示一段字幕,没有时显示另外一段字幕时怎么表示?这时,我们应该把符合条件的字幕放在“处理值2”栏目里面,把不符合条件的字幕放在“字幕”栏目里面。打个比方,在屏幕上第四行、第五格有一个图案,碰它需要有第二个道具,如果有则显示第二段对话,且得到三号道具,没有则显示第三段对话:DESBCSC@B。注意,如果使用了这种信息的格式就不能使用处理元素2。
如果一幅地图里面有多个图形关系到剧情,那么可以把这些信息连起来,如“AB@@CCM@@CD@@DSA@@”,表示在地二行第一格和第四行的第三格都有剧情信息。注意,每一幅地图中所包含剧情信息的图形不能超过7个,否则引擎程序会出错!
2、跳转信息格式:
好了,了解了剧情信息格式之后,让我们来了解一下前面提到的“跳转地图”的含意。动物园一共有9*10幅地图。底下两行存放的是室内的地图,上面则存放的是外景地图:
──门─│┌─植─
─猴└─停│┌┬虎
┌┴─熊│└饲└┐
│游┌─水┐─出┘
└┤│鸟─┤┌─┐
鳄│卖┬─│└┐厕
│桥─┤象┴┐入│
医──饭──办─┘
*********
*********
同样,我们把整个地图看成是一个大集合,每一幅地图看成是一个元素。比如说,把“门”这幅地图看成是一个元素,它在大地图里的第二行的第三格,一行一行地数,每一行有九格,那么它在集合里为第十二个元素,再取元素号+32的ASCII码值(注:这里有别于前面的+64,因为RPG地图很多,127-64幅地图远远不够),再加上需要跳转到的那幅地图的屏幕X、Y轴坐标就可以啦!如“ABC”代表第33幅(“A”对应65号ASCII码,65-32=33)地图屏幕上第三行的第二格。由于RPG的跳转信息很多,所以我们可以在安装程序中拿一个字符串一个接一个地保存跳转信息,把第一个用“A”代替……如:“ACDBEFJIC”为跳转信息字符串,那么第二个跳转信息为“BEF”,在剧情信息中可以用处理值“CB”来实现。
实例请叁看ZOOSC.BAS中第140~150行。
3、地图的制作与显示:
不止十个网友问我:为什么动物园的地图显示得那么快?而且还是在助手里面。答案是我在安装时就生成好了地图保存到随机文件中,玩的时候马上读出来整屏显示,浪费了安装时间而节约了游戏时间。看看ZOOSC.BAS的第170行到180行,这里保存了所有地图要用到的图形的总集合,把地一个图形命名为“A”……再在底下的数据中写下“ABSEHD...”之类的50(文曲星每一屏只能显示50个图形)个字节就可以了。如何让地图数据和剧情信息相结合呢??那么你就在地图数据上后面加一个“,”号,在加上剧情信息就可以了。如:
DATA "ASCDVDFEFGGGGBVVVFFFCCDAAAAACDFVGGDAAAAABBBFFAASAQ","AB@@CSA@@"
注意,如果一幅地图上没有一个图形和剧情有关,那么请用“@”代替剧情信息,引擎程序会自动识别,千万不能空着!
由于我们是把地图先生成好再用随机文件形式写进文件(详见ZOOSC.BAS的第250行到第300行),所以在主程序中我们可以用随机文件的形式打开,令其长度为100,然后100个字地读取数据,在用余兄的批量法显示到屏幕上。由于NC系列和PC2ka系列不支持批量法,你可以用以下方法显示地图:
假定MP$为从地图文件读取的长度为100的字符串变量。
LOCATE 1,1:PRINT LEFT$(MP$,99);
POKE 803,ASC(MID$(MP$,100))
PRINT ;
万事大吉!这个方法对所有机型都试用,只不过速度比批量法稍微慢一点。
4、人物对话保存方法:
在安装程序ZOOSA.BAS和ZOOSB.BAS中,大多数保存的都是人物对话,人物对话可以用DATA来保存和READ读取,详见ZOOSA.BAS。如果想实现两个人的对话,请在第一个人的话后加上一个“*”号,然后再加上第二个人的话。这样,引擎会在显示玩第一句话时停着,再显示下一句话。如果你觉得如果对话写不满的话,你可以重新写一段对话,然后在上一句对话的末尾加上这一句对话的索引号(A、B、C……),引擎会分析并且跳到这段对话来。
5、游戏引擎详解:
激动人心的时刻到了!大家打好前面关于数据剧情方面的基础后,引擎方面就不成问题了!如果你想用我这个引擎发布RPG,你只要写数据文件,只改一点点引擎就可以做出很个性化的RPG,大家看好了!
在这里,我要以最稳定的“动物园历险记”的引擎ZOO.BAS做为叁考,请大家准备好。
打开ZOO.BAS,跳到第9510行,我们先看看变量的含义。
S$~屏幕内容字符串,用批送法声明;
MS$~剧情信息分析变量,用于玩家碰屏幕上的图形时存放当前图形所包含的剧情,长度为7,不包括X、Y坐标;
NG$()~游戏介绍字符串组,用于新游戏时介绍游戏起因;
WD$()~道具名称字符串组;
WI$()~道具图标字符串组;
DD$()~敌人名称字符串组;
DI$()~敌人图标字符串组;
S$()~人物对话(就是前面说的字幕)字符串组;
FN$()~剧情信息字符串组,引擎开始时会读取数据文件放到此字符串组中;
DD()~敌人生死状态数组,0代表生,1代表死;
MT()~玩家道具状态数组,0代表未找到,1代表未使用,2代表已使用;
MZ()~游戏特殊剧情处理数组,前面讲过的,如果符合条件就把这个数组里的某个变量存为1,然后由程序来读取该变量是否为1;
MX:玩家在屏幕上的相对X坐标;
MY:玩家在屏幕上的Y坐标;
MP:玩家所在地图为地图集合里的哪一幅地图;
MLV:玩家等级;
MEX:玩家经验值;
MB:玩家血量。
……
重要的处理代码对应的行号:
20:按键响应子程序;
150:人物对话(字幕)显示子程序;
400:道具列表及道具的选择和使用子程序;
2500:遇敌战斗和升级的子程序;
4600:游戏通关画面及评论代码;
5000:游戏开始标志;
5110~5240:玩家移动控制代码;
5230:随机遇敌代码;
5250~5320:剧情信分离代码,它可以把玩家人物碰到的图形的剧情信息分离出来,由于玩家碰时以确定了XY坐标,所以分离出的信息不含XY坐标元素;
5330~5720:剧情信息分析代码,用于分析我们前面提到的具有相同格式的RPG剧情信息;
5840:地图的读取以及显示代码;
5880:系统菜单选项代码。
好了,下面就让我们来分析一下这些代码中的重要的代码。这可能会让你很晕,但是,为了做出好玩的RPG,晕一些也无所谓了:P。
先来看一下剧情分离代码,从5250行开始。先了解一下原理。由于每一幅地图对应相应的剧情信息(包括“@”空信息)保存在数据文件里,进入引擎一开始会读取它,且每一幅地图的剧情信息用FN$()这个字符串组来保存,由于每一幅地图对应相应的FN$,所以很容易可知道玩家所在地图的FN$,再加以分析就可以了。引擎不是玩家每走一步都判断一次,这样会慢得让你发晕(像天之剑一样),而是如果玩家碰到了障碍物时才读取相应的信息判断。那么如何从该地图的剧情信息中分离出该障碍物所对应的剧情呢?前面我们讲过剧情信息保存的格式,为9个字节,一个连一个地组成该地图包括的剧情。这样,我们可以先读取FN$()前面两个XY的值,看看与玩家所在XY坐标是否相符,如果不相符则跳过这个剧情信息再读取下一个信息的XY,直到相符为止。如果读玩了整段信息还没有找到与玩家XY相同的坐标,BEEP,表示玩家所碰到的图形与剧情无关。
下面来看看代码:
5250 FOR T=1 TO LEN(FN$(MP)) STEP 9
5260 IF ASC(MID$(FN$(MP),T))-64<>TX THEN 5280
5270 IF ASC(MID$(FN$(MP),T+1))-64=TY THEN LSET MS$=MID$(FN$(MP),T+2,7):5300
5280 NEXT
5290 GOTO 5170
5300 TJD=ASC(LEFT$(MS$,1)):TJ=ASC(MID$(MS$,2))-64:SH=ASC(MID$(MS$,3))-64
5310 CLD1=ASC(MID$(MS$,4,1)):CL1=ASC(MID$(MS$,5))-64
5320 CLD2=ASC(MID$(MS$,6,1)):CL2=ASC(MID$(MS$,7))-64
5210~5220行为判断信息中的XY值与玩家所碰的XY是否相等,等的话给临时信息字符串MS$赋除了XY信息之外的其它7个字节信息值,注意这里要用到“LSET”。原因是你用常规方法给一个字符串重新赋值后,GVB会重新分配缓冲区保存且不清除原来的值,多次之后会影响到其它固定的变量的缓冲区,导致其它变量的变化。就像“逃离校园”和“医院惊魂”那样玩久了会出乱码一样。而用LSET可以不移动缓充区直接在原来的缓冲区写。
下面来介绍一下5300行到5320行变量的意思:
TJD~条件标识对应的ASC数值;
TJ~条件值;
SH~字幕值;
CLD1~第一个处理标识对应的ASC值;
CL1~第一个处理值……
注意:在动物园的测试版中,标识符用字符串变量代替,在正式版改为用数变量代替,原因以经讲过了,就是为了防止缓冲区溢出。
从5330行开始就是剧情分析代码了:
5300:分析特殊剧情条件;
5370:分析等级条件;
5375:分析需要某道具的条件;
5420:分析时间条件(如在动物园中不到时间不能进水族馆);
5430:分析没有某特殊剧情的条件……
只要你把标识值变成对应的ASCII码,你就可以知道是什麽意思啦!
那麽进小卖部、芝麻开门怎麽写呢?你只要先把这些代码写入引擎,在用特殊的标识符写进剧情就可以了。如5520~5560行。
从5590~5650行为跳转地图的处理代码,注意读取跳转地图信息时为3个字3个字地读出来(5600行),在加以分析。
让我们再来看看引擎刚开始所需要读取的东西,看9500~9640行。
9530:声明批送法和向缓冲区申请7个字节的剧情分离变量MS$;
9540~9545:声明变量;
9550~9630:读取游戏相关信息,道具图标、道具名字、敌人图标、敌人名字、游戏介绍、人物对话、地头蛇对话、芝麻开门、剧情信息、跳转地图信息。
读取这些东西按照先後顺序附与标识值,如一号道具的图标为WI$(1)。
9640行为用随机方式打开地图文件,因为一个屏幕可以放下100个字符,所以可以令它的读取长度为100,然後100个字100个字地读取任何地图显示到屏幕上,这就是随机文件的好处。这个文件不要关闭,便於可以在程序里面随时读取。


RPG“伏魔剑”源程序
0 :
1 GRAPH
2 DIM WZ(5,21),DM$(4,19):X(6)=3:Y(6)=19
6 MO=100 :GOSUB 4101: GOTO 30
10 IF T$="b" THEN T=1:19
11 IF T$="n" THEN T=2:19
12 IF T$="m" THEN T=3:19
13 IF T$="g" THEN T=4:19
14 IF T$="h" THEN T=5:19
15 IF T$="j" THEN T=6:19
16 T=10
19 RETURN
30 LOCATE 1,1:PRINT " 《伏魔英雄传》"," 超经典RPG互动游戏"," V1.2增强版"
40 LOCATE 4,4:PRINT " 正在读取数据";
50 BOX 40,70,120,73,0,2
54 OPEN "FMYXZDATA" FOR INPUT AS #1
55 INPUT #1,NDFL$: FOR A=1 TO 10:INPUT #1,AE$(A):B=0.808*A+41:LINE B,70,B,73:NEXT
56 FOR A=1 TO 6:INPUT #1,YPM$(A):C=0.808*A+B:LINE C,70,C,73:NEXT :INPUT #1,YDTX$
58 FOR A=1 TO 2:INPUT #1,MAP$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
60 FOR A=1 TO 4:INPUT #1,CD$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
70 FOR A=1 TO 9:INPUT #1,HG$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
80 FOR A=1 TO 7:INPUT #1,ZP$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
90 FOR A=1 TO 9:INPUT #1,DR$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
100 FOR A=1 TO 5:INPUT #1,MZ$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
110 FOR A=1 TO 5:INPUT #1,GZ$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
120 FOR A=1 TO 6:INPUT #1,WG$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
130 FOR A=1 TO 6:INPUT #1,YP$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
140 FOR A=1 TO 4:INPUT #1,MF$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
150 FOR A=-1 TO 1 STEP 2: FOR AA=1 TO 9 STEP 2
151 INPUT #1,DM$((2.5+A)+A*0.5,AA*2+A)
152 B=0.808*A+C:LINE B,70,B,73:NEXT :NEXT
160 FOR A=1 TO 5:INPUT #1,AAA$(A):C=0.808*A+B:LINE C,70,C,73:NEXT
165 FOR A=1 TO 10:INPUT #1,RW$(A):INPUT #1,NA$(A):B=0.808*A+C:LINE B,70,B,73:NEXT
185 FOR A=1 TO 6:INPUT #1,YP(A):C=0.808*A+B:LINE C,70,C,73:INPUT #1,GX(A):NEXT
186 FOR A=1 TO 4:INPUT #1,MF(A):INPUT #1,XG(A):B=0.808*A+C:LINE B,70,B,73:NEXT
189 INPUT #1,AA
190 INPUT #1,BB
191 FOR A=1 TO 4:INPUT #1,BE$(A):NEXT
200 CLOSE #1
210 CLS:PRINT AAA$(1);
220 T$=INKEY$:A=0:GOSUB 10
225 IF T=1 THEN GOSUB 9175
230 ON T GOTO 300,9110,9800:BEEP: GOTO 220
300 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;
305 LOCATE 1,9:PRINT RW$(2);: FOR I=1 TO 3000:NEXT
310 FOR I=2 TO 4:LOCATE I-1,9:PRINT "※";:LOCATE I,9:PRINT RW$(2);
315 FOR J=1 TO 2000:NEXT :NEXT : FOR I=1 TO 1000:NEXT
320 CLS:PRINT CD$(1);:T$=INKEY$
325 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;:LOCATE 4,9:PRINT RW$(2);
330 FOR I=0 TO 80:LINE 0,I,160,I,2: FOR J=1 TO 100:NEXT :NEXT
335 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;:BOX 0,0,160,80,1,2
340 FOR I=0 TO 80:LINE 0,I,160,I,2: FOR J=1 TO 100:NEXT :NEXT
345 FOR I=1 TO 1000:NEXT
350 FOR I=2 TO 4:CLS:PRINT CD$(I);:T$=INKEY$:NEXT
355 FOR I=1 TO 1000:NEXT
360 CLS:PRINT MAP$(2);:POKE 803,BB:PRINT ;: FOR I=1 TO 1000:NEXT
365 LOCATE 5,11:PRINT RW$(3);: FOR I=1 TO 2000:NEXT
370 LOCATE 5,11:PRINT " ";:LOCATE 4,11:PRINT RW$(3);
380 FOR J=1 TO 2000:NEXT
381 LOCATE 4,11:PRINT " ";:LOCATE 3,11:PRINT RW$(3);: FOR I=1 TO 2000:NEXT
385 FOR I=1 TO 3:CLS:PRINT HG$(I);:T$=INKEY$:NEXT
390 CLS:PRINT MAP$(2);:POKE 803,BB:PRINT ;
395 LOCATE 3,11:PRINT RW$(3);: FOR I=1 TO 2000:NEXT
400 LOCATE 4,11:PRINT RW$(3);:LOCATE 3,11:PRINT " ";: FOR I=1 TO 2000:NEXT
405 LOCATE 4,13:PRINT RW$(3);:LOCATE 4,11:PRINT " ";: FOR I=1 TO 2000:NEXT
410 LOCATE 5,11:PRINT RW$(1);: FOR I=1 TO 2000:NEXT
415 LOCATE 4,11:PRINT RW$(1);:LOCATE 5,11:PRINT " ";
420 FOR J=1 TO 2000:NEXT
421 LOCATE 3,11:PRINT RW$(1);:LOCATE 4,11:PRINT " ";
422 FOR J=1 TO 2000:NEXT
425 FOR I=4 TO 9:CLS:PRINT HG$(I);:T$=INKEY$:NEXT
500 X=3:Y=11:Q=Q+1:B=1
505 GET #1,1
506 CLS:PRINT MAP$;:LOCATE X,Y:PRINT RW$(1);
507 LOCATE 5,1:PRINT DM$(X,Y);" ";
510 T=ASC(INKEY$):IF (T>19 AND T<24) OR (T=13 OR T=113) THEN 515 ELSE BEEP:510
515 IF T=20 THEN X=X-1:IF X<1 THEN BEEP:X=1:505
520 IF T=21 THEN X=X+1:IF X>4 THEN BEEP:X=4:505
525 IF T=22 THEN Y=Y+2:IF Y>19 THEN BEEP:Y=19:505
530 IF T=23 THEN Y=Y-2:IF Y<1 THEN BEEP:Y=1:505
535 IF T=113 THEN GOSUB 9300: GOTO 505
536 IF T>13 THEN 505
540 IF X=4 THEN 570
541 IF X=2 OR X=3 THEN BEEP:505
545 IF Y=1 THEN 600
550 IF Y=5 THEN 700
555 IF Y=9 THEN 800
560 IF Y=13 THEN 4500
561 IF Y=17 THEN 590
565 BEEP: GOTO 505
570 IF ((Y=3)+(Y=7)+(Y=15)+(Y=19)) THEN 590
575 IF Y=11 THEN 576 ELSE 580
576 IF A=0 THEN 2000 ELSE IF A=1 THEN 5000
580 BEEP: GOTO 505
590 CLS:PRINT "请问你找谁?";:T$=INKEY$: GOTO 505
600 G=1
601 C=17:D=16:PP=1
602 CLS:PRINT "********";DM$(X,Y);:LOCATE 1,13:PRINT "********";YDTX$;
606 LOCATE 5,3:PRINT "× × ";
611 LOCATE 5,1:PRINT YPM$(PP);"×";YPS(PP);
612 LOCATE 5,13:PRINT MO;:BOX C,D,C+48,D+15,1,2:E=C:F=D
620 T=ASC(INKEY$):IF (T<24 AND T>19) OR T=13 OR T=27 THEN 625 ELSE BEEP:620
625 IF T=20 THEN D=D-16:PP=PP-1:IF D<16 THEN D=48:PP=PP+3
630 IF T=21 THEN D=D+16:PP=PP+1:IF D>48 THEN D=16:PP=PP-3
635 IF T=23 THEN C=C-80:PP=PP-3:IF C<17 THEN C=97:PP=PP+6
640 IF T=22 THEN C=C+80:PP=PP+3:IF C>97 THEN C=17:PP=PP-6
645 IF T=27 THEN 505
647 IF T>13 THEN 678
650 IF G=2 THEN 4550
655 IF MO665 MO=MO-YP(PP):YPS(PP)=YPS(PP)+1
675 IF YPS(1)+YPS(2)+YPS(3)+YPS(4)+YPS(5)+YPS(6)>50 THEN YPS(PP)=YPS(PP)-1:MO=MO+YP(PP):680
678 BOX E,F,E+48,F+15,1,2: GOTO 606
680 LOCATE 5,1:PRINT "携带的物品太多了!";:T$=INKEY$: GOTO 606
700 CLS:PRINT "********客栈********";AAA$(2);
710 T$=INKEY$
720 IF T$="n" THEN 505 ELSE IF T$<>"y" THEN BEEP:710
730 IF MO<200 THEN CLS:PRINT "对不起!你没那么多钱啊!我们这不赊账!再见!":731 ELSE 735
731 T$=INKEY$: GOTO 505
735 FOR II=-1 TO 0: FOR I=0 TO 80: FOR J=1 TO 100:NEXT :LINE 0,I,160,I,(-2)*II:NEXT :NEXT
740 MO=MO-200:MP=MPP:HP=HPP:CLS:PRINT "欢迎下次再来!":T$=INKEY$: GOTO 505
800 IF Q>5 THEN QT=QT+1:IF QT>3 THEN QT=3
801 IF QT=3 THEN CLS:PRINT ZP$(7):T$=INKEY$:505
805 IF Q=5 AND QT=2 THEN CLS:PRINT ZP$(6);:T$=INKEY$:QT=3:505
820 CLS:PRINT ZP$(Q);:T$=INKEY$: GOTO 505
1100 G=G+7:H=H+7
1120 FOR J=1 TO 0 STEP -1: FOR JJ=0 TO 7:BOX G-JJ,H-JJ,G+JJ,H+JJ,1,J
1130 FOR JJJ=1 TO 150:NEXT :NEXT :NEXT
1135 RETURN
1200 BOX 144,32,159,47:LOCATE 5,1:PRINT NB$(6);KP(6);"/";KPP(6);: FOR I=1 TO 2500
1205 NEXT :IF Q=10 THEN 1206 ELSE 1210
1206 I=RND(1):IF I>0.4 THEN 1240
1207 I=ABS(X(1)-X(6)):J=ABS(Y(1)-Y(6)):IF I=0 AND J=2 THEN 1245
1208 IF I=1 AND J=0 THEN 1245 ELSE 2019
1210 IF Y(1)=15 AND X(1)=3 THEN 1240
1211 IF Y(1)=17 THEN 1220
1212 IF Y(1)=19 THEN 1230
1213 GOTO 2019
1220 IF X(1)=2 OR X(1)=4 THEN 1240
1221 IF X(1)=3 THEN 1245
1222 GOTO 2019
1230 IF X(1)<>1 THEN 1245
1235 IF X(1)>14 THEN 1240
1240 ON Q GOTO 1500,1600,1500,1600,1500,1600,1500,1600,1500,1700
1241 GG=Q*86+INT(RND(1)*(10-Q)*8): GOTO 2940
1245 GG=Q*69+INT(RND(1)*(10-Q)*3): GOTO 8400
1500 II=0:H=X(1)*16-8:G=Y(1)*8-3
1510 FOR I=20 TO 160 STEP 2:II=II+0.2:ELLIPSE I,31,II,II*2
1520 FOR J=1 TO 50:NEXT :NEXT
1524 FOR I=-1 TO 0: FOR J=0 TO 7:CIRCLE G+4,H,J,1,-1*I: FOR II=1 TO 50:NEXT :NEXT :NEXT
1530 GOSUB 9995: GOTO 1241
1600 FOR II=20 TO 120:I=0.23*II:J=0.08*II
1610 DRAW II,35-I:DRAW II,33-J:DRAW II,29+J:DRAW II,27+I
1620 FOR JJ=1 TO 5:NEXT :NEXT
1630 FOR II=1 TO 0 STEP -1: FOR I=0 TO 7
1640 CIRCLE 120,8,I,1,II:CIRCLE 120,24,I,1,II:CIRCLE 120,40,I,1,II:CIRCLE 120,56,I,1,II
1650 CIRCLE 136,8,I,1,II:CIRCLE 136,24,I,1,II:CIRCLE 136,40,I,1,II:CIRCLE 136,56,I,1,II
1660 CIRCLE 152,8,I,1,II:CIRCLE 152,24,I,1,II:CIRCLE 152,40,I,1,II:CIRCLE 152,56,I,1,II
1680 NEXT :NEXT :GOSUB 9995: GOTO 1241
1700 FOR I=144 TO 80 STEP -1:DRAW I,I*0.625-50:FOR II=1 TO 50 :NEXT:NEXT
1702 FOR II=0 TO 60:LINE 76,II,84,II:FOR I=1 TO 50:NEXT:NEXT
1705 FOR II=80 TO 160:LINE II,52,II,60:LINE 160-II,52,160-II,60:NEXT
1710 FOR II=56 TO 6 STEP -16:FOR J=1 TO 0 STEP -1:FOR JJ=0 TO 7:FOR I= 8 TO 160 STEP 16
1715 CIRCLE I,II,JJ,1,J:NEXT:NEXT:NEXT:GOSUB 9995:NEXT
1720 GOSUB 9995:GOTO 1241
1999 IF Q=10 THEN KP(6)=40000:KPP(6)=KP(6):2001 ELSE 2001
2000 X(1)=1:Y(1)=1:R=R+1:RR=0:NPC$(1)=RW$(1):KP(6)=Q*2678:KPP(6)=KP(6): GOTO 1999
2001 FOR I=2 TO 5:KP(I)=Q*50+R*10:J=INT(RND(1)*7)+4:NPC$(I)=RW$(J):NB$(I)=NA$(J):X(I)=I-1
2002 KPP(I)=KP(I):Y(I)=INT(RND(1)*10)*2+1:IF Y(I)=Y(1) AND X(I)=X(1) THEN 2002
2003 IF R=INT(R/5)*5 AND Y(I)=Y(6) AND X(I)=X(6) THEN 2002
2004 NEXT :GJL=Q*14+R*4:VW=4:WV=4:S=0
2005 GET #1,R+1:B=0:IF R=20 THEN A=1
2009 IF R<>INT(R/5)*5 THEN 2019
2010 NB$(6)=AE$(Q):IF R<50 THEN 2015
2011 IF RR=1 THEN 2019 ELSE FOR I=1 TO 5:CLS:PRINT MZ$(I);:T$=INKEY$:NEXT :RR=0: GOTO 2019
2015 IF RR=0 THEN CLS:PRINT DR$(Q):T$=INKEY$:RR=1
2019 GOSUB 9995:IF VW=0 THEN RR=1:2001
2020 IF KP(6)>0 THEN 2025 ELSE CLS:PRINT AE$(Q);"被击败。"
2021 IF Q<5 THEN PRINT "得到";AE$(Q):LOCATE 2,9:PRINT "灵珠";BE$(Q);: GOTO 2023
2022 IF R=50 THEN T$=INKEY$: GOTO 5100
2023 T$=INKEY$: GOTO 500
2025 FOR I=1 TO 4: FOR J=1 TO 19 STEP 2:WZ(I,J)=0:NEXT :NEXT
2028 FOR I=2 TO 5:IF KP(I)=0 THEN 2029 ELSE WZ(X(I),Y(I))=I
2029 NEXT :IF R=INT(R/5)*5 THEN WZ(3,19)=6
2041 S=S+1:IF S=6 AND R=INT(R/5)*5 THEN 1200
2042 IF S=1 THEN 3000 ELSE IF S>5 THEN S=0:2041 ELSE IF KP(S)=0 THEN 2041
2043 GG=GJL: GOTO 2051
2050 GOTO 2019
2051 GOSUB 9994
2052 LOCATE 5,1:PRINT NB$(S);KP(S);"/";KPP(S);" ";:C=Y(S)*8-8:D=X(S)*16-16
2053 BOX C,D,C+15,D+15:IF X(S)>X(1) AND Y(S)>Y(1) THEN 2100
2055 IF X(S)>X(1) AND Y(S)=Y(1) THEN 2200
2060 IF X(S)>X(1) AND Y(S)2065 IF X(S)=X(1) AND Y(S)>Y(1) THEN 2400
2070 IF X(S)=X(1) AND Y(S)2075 IF X(S)Y(1) THEN 2600
2080 IF X(S)2085 IF X(S)2100 E=ABS(X(1)-X(S)):F=ABS(Y(1)-Y(S))/2
2110 IF E+F>3 THEN 2150
2120 IF E=1 AND F=2 THEN GOSUB 7000: GOTO 8800
2130 IF E=1 AND F=1 THEN GOSUB 7050: GOTO 8800
2140 IF E=2 AND F=1 THEN GOSUB 7100: GOTO 8800
2150 IF E=1 THEN GOSUB 7600: GOTO 2050
2160 IF E>1 THEN GOSUB 7650: GOTO 2050
2200 E=ABS(X(1)-X(S))
2220 IF E=1 THEN 8800
2230 IF E=2 THEN GOSUB 8000: GOTO 8800
2240 IF E=3 THEN GOSUB 8050: GOTO 8800
2300 E=ABS(X(1)-X(S)):F=ABS(Y(1)-Y(S))/2
2310 IF E+F>3 THEN 2350
2320 IF E=1 AND F=2 THEN GOSUB 7150: GOTO 8800
2330 IF E=1 AND F=1 THEN GOSUB 7200: GOTO 8800
2340 IF E=2 AND F=1 THEN GOSUB 7250: GOTO 8800
2350 IF E=1 THEN GOSUB 7700: GOTO 2050
2360 IF E>1 THEN GOSUB 7750: GOTO 2050
2400 F=ABS(Y(1)-Y(S))/2:E=0
2420 IF F=1 THEN E=1:8800
2430 IF F=2 THEN GOSUB 8100: GOTO 8800
2440 IF F=3 THEN GOSUB 8150: GOTO 8800
2450 GOSUB 8600: GOTO 2050
2500 F=ABS(Y(1)-Y(S))/2:E=0
2520 IF F=1 THEN E=1:8800
2530 IF F=2 THEN GOSUB 8200: GOTO 8800
2540 IF F=3 THEN GOSUB 8250: GOTO 8800
2550 GOSUB 8500: GOTO 2050
2600 E=ABS(X(1)-X(S)):F=ABS(Y(1)-Y(S))/2
2610 IF E+F>3 THEN 2650
2620 IF E=1 AND F=2 THEN GOSUB 7300: GOTO 8800
2630 IF E=1 AND F=1 THEN GOSUB 7350: GOTO 8800
2640 IF E=2 AND F=1 THEN GOSUB 7400: GOTO 8800
2650 IF E=1 THEN GOSUB 7800: GOTO 2050
2660 IF E>1 THEN GOSUB 7850: GOTO 2050
2700 E=ABS(X(1)-X(S)):F=ABS(Y(1)-Y(S))/2
2720 IF E=1 THEN 8800
2730 IF E=2 THEN GOSUB 8300: GOTO 8800
2740 IF E=3 THEN GOSUB 8350: GOTO 8800
2800 E=ABS(X(1)-X(S)):F=ABS(Y(1)-Y(S))/2
2810 IF E+F>3 THEN 2850
2820 IF E=1 AND F=2 THEN GOSUB 7450: GOTO 8800
2830 IF E=1 AND F=1 THEN GOSUB 7500: GOTO 8800
2840 IF E=2 AND F=1 THEN GOSUB 7550: GOTO 8800
2850 IF E=1 THEN GOSUB 7900: GOTO 2050
2860 IF E>1 THEN GOSUB 7950: GOTO 2050
2900 X(S)=X(S)-1:GOSUB 9995: FOR JJ=1 TO 1000:NEXT :RETURN
2910 X(S)=X(S)+1:GOSUB 9995: FOR JJ=1 TO 1000:NEXT :RETURN
2920 Y(S)=Y(S)-2:GOSUB 9995: FOR JJ=1 TO 1000:NEXT :RETURN
2930 Y(S)=Y(S)+2:GOSUB 9995: FOR JJ=1 TO 1000:NEXT :RETURN
2940 GOSUB 9995:HPPP=GG+INT(RND(1)*DJ/2):HP=HP-HPPP:IF HP>0 THEN 2950 ELSE 2960
2950 GOSUB 9994:LOCATE 5,1:PRINT "你的体力下降了";HPPP;: FOR I=1 TO 1500:NEXT : GOTO 2025
2960 CLS:LOCATE 2,5:PRINT "你的体力不支,"," 昏了过去……"
2970 FOR I=0 TO 80:LINE 0,I,160,I,2: FOR J=0 TO 100:NEXT :NEXT
2980 T$=INKEY$:CLOSE #1:POKE 199,155
3000 W=0:V=0
3005 GOSUB 9994
3006 LOCATE 5,1:PRINT NA$(1);"HP:";HP;"/";HPP;
3010 T=ASC(INKEY$):IF (T>19 AND T<24) OR (T=13 OR T=113) THEN 3020 ELSE BEEP:3010
3020 IF T=13 THEN T=24
3025 IF T=113 THEN T=25
3030 ON T-19 GOTO 3100,3200,3400,3300,3600,3040
3040 GOSUB 9900: GOSUB 9995:GOTO 3006
3100 X(S)=X(S)-1:W=W-1:IF X(S)=0 THEN BEEP:X(S)=1:W=W+1
3102 IF ABS(W)+ABS(V)>3 OR WZ(X(S),Y(S))>0 THEN X(S)=X(S)+1:W=W+1:BEEP:3010
3110 GOSUB 9995: GOTO 3010
3200 X(S)=X(S)+1:W=W+1:IF X(S)=5 THEN BEEP:X(S)=4:W=W-1
3210 IF ABS(W)+ABS(V)>3 OR WZ(X(S),Y(S))>0 THEN X(S)=X(S)-1:W=W-1:BEEP:3010
3220 IF X(S)=4 AND Y(S)=19 AND R<>INT(R/5)*5 THEN RR=0:2000
3226 GOSUB 9995: GOTO 3010
3300 Y(S)=Y(S)-2:V=V-1:IF Y(S)<1 THEN BEEP:Y(S)=1:V=V+1
3310 IF ABS(W)+ABS(V)>3 OR WZ(X(S),Y(S))>0 THEN Y(S)=Y(S)+2:V=V+1:BEEP:3010
3326 GOSUB 9995: GOTO 3010
3400 Y(S)=Y(S)+2:V=V+1:IF Y(S)>19 THEN BEEP:Y(S)=19:V=V-1
3410 IF ABS(W)+ABS(V)>3 OR WZ(X(S),Y(S))>0 THEN Y(S)=Y(S)-2:V=V-1:BEEP:3010
3425 IF X(S)=4 AND Y(S)=19 AND R<>INT(R/5)*5 THEN RR=0:2000
3426 GOSUB 9995: GOTO 3010
3500 GOSUB 9400: GOTO 3005
3600 GOSUB 9994:LOCATE 5,1:PRINT "①攻 ②法 ③物 ④防";
3610 T$=INKEY$:IF T$=CHR$(27) THEN 3005
3611 GOSUB 10:ON T GOTO 3670,3900,3660,3620:BEEP: GOTO 3610
3620 U=0: GOTO 2019
3660 GOSUB 9400:GOSUB 9995:IF T$="Y" THEN 2019 ELSE 3600
3670 PP=1=5
3700 IF X(1)-1=0 THEN 3702
3701 IF WZ(X(1)-1,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)-1,Y(1))
3702 IF X(1)+1=5 THEN 3704
3703 IF WZ(X(1)+1,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)+1,Y(1))
3704 IF Y(1)-2<0 THEN 3706
3705 IF WZ(X(1),Y(1)-2)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)-2)
3706 IF Y(1)+2=21 THEN 3708
3707 IF WZ(X(1),Y(1)+2)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)+2)
3708 K=1
3709 IF PP=2 AND MP3712 IF U>0 THEN 3714
3713 BEEP:LOCATE 5,1:PRINT "目标不在范围内! ";:T$=INKEY$: GOTO 3600
3714 GOSUB 9994:LOCATE 5,1:PRINT NB$(UU(K));
3715 PRINT KP(UU(K));"/";KPP(UU(K));:G=Y(UU(K))*8-8
3716 H=X(UU(K))*16-16:BOX G,H,G+15,H+15
3717 T=ASC(INKEY$):IF (T>19 AND T<24) OR (T=27 OR T=13) THEN 3719 ELSE BEEP:3717
3719 IF T=27 THEN U=0:3600
3720 IF U=0 THEN 3712
3725 IF T<>13 THEN 3735
3726 IF T=13 THEN ON PP GOTO 3730,6001
3730 IF T=13 THEN GOSUB 1100:GGLL=GGL
3731 KP=KP(UU(K)):KPPP=GGLL+INT(RND(1)*DJ)
3732 KP(UU(K))=KP(UU(K))-KPPP:IF KP(UU(K))>0 THEN 3733 ELSE 3790
3733 GOSUB 9995:GOSUB 9994:LOCATE 5,1:PRINT NPC$(UU(K));"体力下降了";KPPP;:T$=INKEY$: GOTO 3820
3735 K=K+1:BOX G,H,G+15,H+15,0,2:IF K>U THEN K=1
3740 ON P GOTO 3714,3714,6536,6940,3714
3790 WV=VW
3791 KP(UU(K))=0:JP=KPP(UU(K))+INT(RND(1)*DJ):JY=JY+JP:WZ(X(UU(K)),Y(UU(K)))=0:VW=VW-1
3792 I=INT(RND(1)*200):IF I>49 THEN MO=MO+INT(KPP(UU(K))/7)+INT(RND(1)*(DJ/2)):PC$="金币"
3795 IF I=49 THEN YPS(3)=YPS(3)+1:PC$=YP$(3)
3797 IF I=48 THEN YPS(6)=YPS(6)+1:PC$=YP$(6)
3801 IF I<48 AND I>26 THEN YPS(4)=YPS(4)+1:PC$=YP$(4)
3802 IF I<27 AND I>5 THEN YPS(1)=YPS(1)+1:PC$=YP$(1)
3803 IF I<6 AND I>2 THEN YPS(2)=YPS(2)+1:PC$=YP$(2)
3805 IF I<3 THEN YP(5)=YP(5)+1:PC$=YP$(5)
3806 IF UU(K)<6 THEN 3808 ELSE LOCATE 5,1:PRINT NB$(6);:LOCATE 5,9:PRINT "被打败了!";
3807 T$=INKEY$:LOCATE 5,1: GOTO 3809
3808 GOSUB 9995:LOCATE 5,1:PRINT NB$(UU(K));"被打败了!";:T$=INKEY$:LOCATE 5,1
3809 PRINT "得到";PC$;" ";:T$=INKEY$:LOCATE 5,1:PRINT "获得经验值";JP;
3810 T$=INKEY$:IF JY>=JYZ THEN GOSUB 4100
3820 IF P=3 THEN 6536 ELSE IF P=4 THEN 6940 ELSE 3620
3900 P=1:GOSUB 9994
3910 LOCATE 5,1:PRINT MF$(P);" ";
3920 T=ASC(INKEY$):IF T=20 OR T=21 OR T=13 OR T=27 THEN 3921 ELSE 3920
3921 IF T=21 THEN P=P+1:IF P>4 THEN BEEP:P=4:3920
3922 IF T=20 THEN P=P-1:IF P<1 THEN BEEP:P=1:3920
3923 IF T=27 THEN GOSUB 9995: GOTO 3600
3924 IF T<>13 THEN 3910
3925 ON P GOTO 6000,6400,6400,6900
4100 I=HPP:J=MPP:G=GGL:GOSUB 4101:GOTO 4110
4101 DJ=DJ+1:JYZ=DJ*300-200:HPP=DJ*38+50-DJ*4:MPP=DJ*19+(40-DJ*6):GGL=DJ*21+(40-DJ*9)
4105 HP=HPP:MP=MPP:RETURN
4110 LOCATE 5,1:PRINT "你升级了!";DJ;"级 ";:T$=INKEY$:JY=0:GOSUB 9994
4111 LOCATE 5,1:PRINT "体力+";HPP-I;:T$=INKEY$:GOSUB 9994
4112 LOCATE 5,1:PRINT "法力+";MPP-J;:T$=INKEY$:GOSUB 9994
4113 LOCATE 5,1:PRINT "攻击力+";GGL-G;:T$=INKEY$
4115 RETURN
4500 G=2: GOTO 601
4550 IF YPS(PP)<=0 THEN BEEP: GOTO 620
4560 YPS(PP)=YPS(PP)-1:MO=MO+INT(YP(PP)/2): GOTO 606
5000 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;
5002 LOCATE 1,3:PRINT "※※※※※※※";:LOCATE 3,1:PRINT RW$(1);
5004 LOCATE 2,19:PRINT RW$(2);:LOCATE 3,19:PRINT "";: FOR I=1 TO 5000:NEXT
5006 FOR I=1 TO 2:CLS:PRINT GZ$(I);:T$=INKEY$:NEXT
5008 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;
5010 LOCATE 1,3:PRINT "※※※※※※※";:LOCATE 3,1:PRINT RW$(1);
5011 LOCATE 3,19:PRINT "";:LOCATE 2,19:PRINT RW$(2);
5012 LOCATE 3,3:PRINT "";:LOCATE 4,3:PRINT "";: FOR I=1 TO 2000:NEXT
5014 FOR I=3 TO 13 STEP 2
5016 LOCATE 3,I:PRINT "※";:LOCATE 4,I:PRINT "※";
5020 FOR J=1 TO 2000:NEXT :NEXT
5022 LOCATE 3,15:PRINT "※※※";:LOCATE 4,15:PRINT "※※※";: FOR I=1 TO 5000
5024 NEXT : FOR I=3 TO 9 STEP 2:II=20-I
5026 LOCATE 3,I-2:PRINT "※";
5028 LOCATE 2,II:PRINT "※";
5030 FOR J=1 TO 2000:NEXT :NEXT :LOCATE 3,9:PRINT "※";
5031 LOCATE 2,9:PRINT RW$(1): FOR I=1 TO 5000:NEXT
5032 FOR I=3 TO 5:CLS:PRINT GZ$(I):T$=INKEY$:NEXT
5034 CLS:PRINT MAP$(2);:POKE 803,BB:PRINT ;
5036 FOR I=5 TO 2 STEP -1: FOR J=1 TO 2000:NEXT :II=I+1:IF II>5 THEN II=5
5038 LOCATE II,11:PRINT " ";:LOCATE I,11:PRINT RW$(3);:NEXT
5040 CLS:PRINT WG$(1);:T$=INKEY$
5042 CLS:PRINT MAP$(2);:POKE 803,BB:PRINT ;:LOCATE 3,11:PRINT RW$(3);
5044 FOR I=1 TO 3: FOR J=1 TO 2000:NEXT
5046 ON I GOTO 5048,5050,5052
5048 LOCATE 3,11:PRINT RW$(3): GOTO 5054
5050 LOCATE 4,11:PRINT RW$(3);:LOCATE 3,11:PRINT " ";: GOTO 5054
5052 LOCATE 4,13:PRINT RW$(3);:LOCATE 4,11:PRINT " ";
5054 NEXT :LOCATE 5,11:PRINT RW$(2);: FOR I=1 TO 2000:NEXT
5055 LOCATE 4,11:PRINT RW$(2);:LOCATE 5,11:PRINT RW$(1);: FOR I=1 TO 2000:NEXT
5056 LOCATE 3,11:PRINT RW$(2);:LOCATE 4,11:PRINT RW$(1):LOCATE 5,11:PRINT " ";
5058 FOR I=1 TO 2000:NEXT
5060 FOR I=2 TO 6:CLS:PRINT WG$(I);:T$=INKEY$:NEXT
5062 A=0:X=3:Y=11:QT=2 : GOTO 505
5100 CLS:PRINT MAP$(1);:POKE 803,AA:PRINT ;
5102 LOCATE 1,3:PRINT "※※※※※※※";
5104 LOCATE 4,1:PRINT RW$(2);:LOCATE 4,19:PRINT RW$(1);: FOR I=1 TO 2000:NEXT
5106 FOR I=3 TO 9 STEP 2:II=20-I
5108 LOCATE 4,I-2:PRINT "※";:LOCATE 4,II:PRINT "※";
5110 FOR J=1 TO 2000:NEXT :NEXT
5112 FOR I=1 TO 10:CIRCLE 79+I,48-I*3,I,1:CIRCLE 81-I,48-I*3,I,1
5114 FOR J=1 TO 400:NEXT :NEXT :T$=INKEY$:LOCATE 3,8:PRINT "全剧终":T$=INKEY$
5116 FOR I=0 TO 80:LINE I,0,I,80:LINE 160-I,0,160-I,80:NEXT : FOR I=1 TO 2000:NEXT
5118 GOTO 9800
6000 PP=2: GOTO 3700
6001 G=Y(UU(K))*8-5:H=X(UU(K))*16-8
6010 FOR I=0 TO 63:LINE G,I,G+8,I: FOR J=1 TO 50:NEXT :NEXT
6020 FOR I=-1 TO 0: FOR J=0 TO 7:CIRCLE G+4,H,J,1,-1*I: FOR II=1 TO 50:NEXT :NEXT :NEXT
6030 GGLL=XG(1):MP=MP-MF(1):GOSUB 9995: GOTO 3731
6100 G=Y(UU(K))*8:H=X(UU(K))*16-8: FOR I=7 TO 55 STEP 4
6110 CIRCLE G,I,8,0,1: FOR J=1 TO 150:NEXT :NEXT
6115 FOR I=-1 TO 0: FOR J=0 TO 7:CIRCLE G,H,J,1,-1*I
6120 FOR II=1 TO 100:NEXT :NEXT :NEXT
6130 RETURN
6200 G(1)=Y(UU(K))*8-2:H(1)=X(UU(K))*16-8
6210 G(2)=G(1)-16:H(2)=H(1):IF G(2)<0 THEN G(2)=G(1)
6220 G(3)=G(1):H(3)=H(1)-16:IF H(3)<0 THEN H(3)=H(1)
6230 G(4)=G(1):H(4)=H(1)+16:IF H(4)>60 THEN H(4)=H(1)
6240 G(5)=G(1)+16:H(5)=H(1):IF G(5)>160 THEN G(5)=G(1)
6250 FOR I=-1 TO 0: FOR J=0 TO 7: FOR II=1 TO 5
6255 CIRCLE G(II),H(II),J,1,-1*I:NEXT
6260 FOR JJ=1 TO 20:NEXT :NEXT :NEXT
6270 RETURN
6300 GOTO 6900
6310 FOR III=1 TO 10:G=INT(RND(1)*120)+20:H=INT(RND(1)*24)+20:I=INT(RND(1)*10)+10
6315 FOR II=1 TO 0 STEP -1: FOR J=0 TO I:CIRCLE G,H,J,1,II
6320 NEXT :NEXT :GOSUB 9995:NEXT :MP=MP-MF(4)
6330 RETURN
6400 U=0:K=1:IF MP6402 U=0:K=1:IF X(1)-2<1 THEN 6405
6404 IF WZ(X(1)-2,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)-2,Y(1))
6405 IF X(1)-1=0 THEN 6416
6406 IF Y(1)-2<0 THEN 6410
6408 IF WZ(X(1)-1,Y(1)-2)<>0 THEN U=U+1:UU(U)=WZ(X(1)-1,Y(1)-2)
6410 IF WZ(X(1)-1,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)-1,Y(1))
6412 IF Y(1)+2>19 THEN 6416
6414 IF WZ(X(1)-1,Y(1)+2)<>0 THEN U=U+1:UU(U)=WZ(X(1)-1,Y(1)+2)
6416 IF Y(1)-4<0 THEN 6420
6418 IF WZ(X(1),Y(1)-4)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)-4)
6420 IF Y(1)-2<0 THEN 6424
6422 IF WZ(X(1),Y(1)-2)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)-2)
6424 IF Y(1)+2>19 THEN 6432
6426 IF WZ(X(1),Y(1)+2)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)+2)
6428 IF Y(1)+4>19 THEN 6432
6430 IF WZ(X(1),Y(1)+4)<>0 THEN U=U+1:UU(U)=WZ(X(1),Y(1)+4)
6432 IF X(1)+1>4 THEN 6500
6434 IF Y(1)-2<0 THEN 6438
6436 IF WZ(X(1)+1,Y(1)-2)<>0 THEN U=U+1:UU(U)=WZ(X(1)+1,Y(1)-2)
6438 IF WZ(X(1)+1,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)+1,Y(1))
6440 IF Y(1)+2>19 THEN 6444
6442 IF WZ(X(1)+1,Y(1)+2)<>0 THEN U=U+1:UU(U)=WZ(X(1)+1,Y(1)+2)
6444 IF X(1)+2>4 THEN 6500
6446 IF WZ(X(1)+2,Y(1))<>0 THEN U=U+1:UU(U)=WZ(X(1)+2,Y(1))
6500 IF K>U THEN K=1:6506 ELSE IF U>0 THEN 6506
6502 BEEP:LOCATE 5,1:PRINT "目标不在范围内!";:T$=INKEY$: GOTO 3600
6506 GOSUB 9995:LOCATE 5,1:PRINT NB$(UU(K));KP(UU(K));"/";
6507 PRINT KPP(UU(K));:G=Y(UU(K))*8-8:H=X(UU(K))*16-16
6508 BOX G,H,G+15,H+15:IF P=2 THEN 6526
6510 IF X(UU(K))-1=0 THEN 6514
6512 BOX G,H-16,G+15,H-1
6514 IF X(UU(K))+1=5 THEN 6518
6516 BOX G,H+16,G+15,H+31
6518 IF Y(UU(K))-2<0 THEN 6522
6520 BOX G-16,H,G-1,H+15
6522 IF Y(UU(K))+2>19 THEN 6526
6524 BOX G+16,H,G+31,H+15
6526 T=ASC(INKEY$):IF (T>19 AND T<24) OR T=13 OR T=27 THEN 6528 ELSE BEEP:6526
6528 IF T=27 THEN U=0:3600
6530 IF T<>13 THEN K=K+1:6500
6534 IF T=13 AND P=2 THEN GOSUB 6100:GGLL=XG(2):MP=MP-MF(2): GOTO 3731
6535 IF T=13 AND P=3 THEN GOSUB 6200:GGLL=XG(3):M=0:N=UU(K):MP=MP-MF(3): GOTO 3731
6536 M=M+1:IF M>4 THEN 3620
6537 FOR I=1 TO 4: FOR J=1 TO 19 STEP 2:WZ(I,J)=0:NEXT :NEXT
6538 FOR I=2 TO 6:WZ(X(I),Y(I))=I
6539 NEXT :ON M GOTO 6540,6544,6548,6552
6540 IF X(N)>1 THEN IF WZ(X(N)-1,Y(N))>1 THEN UU(K)=WZ(X(N)-1,Y(N)):3731
6542 GOTO 6536
6544 IF X(N)<4 THEN IF WZ(X(N)+1,Y(N))>1 THEN UU(K)=WZ(X(N)+1,Y(N)):3731
6546 GOTO 6536
6548 IF Y(N)>1 THEN IF WZ(X(N),Y(N)-2)>1 THEN UU(K)=WZ(X(N),Y(N)-2):3731
6550 GOTO 6536
6552 IF Y(N)<19 THEN IF WZ(X(N),Y(N)+2)>1 THEN UU(K)=WZ(X(N),Y(N)+2):3731
6554 GOTO 3620
6900 IF MP6910 GOSUB 6310:M=0
6915 M=M+1:IF KP(M)=0 THEN 6940
6920 IF M=6 AND R<>INT(R/5)*5 THEN 6940
6930 GGLL=XG(4):UU(K)=M: GOTO 3731
6940 IF M<7 THEN 6915 ELSE 3620
7000 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7010 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2920:GOSUB 2900:RETURN
7020 IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2920:E=0:RETURN
7030 GOSUB 2900:E=0:RETURN
7050 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2920:RETURN
7060 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2900:RETURN
7080 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2900:GOSUB 2900:E=0:RETURN
7090 IF Y(1)>1 THEN IF WZ(X(1)+1,Y(1)-2)=0 THEN GOSUB 2920:GOSUB 2920:E=0:RETURN
7095 E=0:RETURN
7100 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2920:GOSUB 2900:E=1:RETURN
7110 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2900:GOSUB 2900:E=1:RETURN
7120 IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2900:E=0:RETURN
7130 GOSUB 2920:E=0:RETURN
7150 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7160 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2930:GOSUB 2900:RETURN
7170 IF WZ(X(1)+1,Y(1)-2)=0 THEN GOSUB 2930:E=0:RETURN
7180 GOSUB 2900:E=0:RETURN
7190 E=0:RETURN
7200 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2930:RETURN
7210 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2900:RETURN
7230 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)-2)=0 THEN GOSUB 2900:GOSUB 2900:E=0:RETURN
7240 IF Y(1)<19 THEN IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2930:GOSUB 2930:E=0:RETURN
7245 E=0:RETURN
7250 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2930:GOSUB 2900:E=1:RETURN
7260 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2900:GOSUB 2900:E=1:RETURN
7270 IF WZ(X(1)+2,Y(1)+2)=0 THEN GOSUB 2930:RETURN
7280 IF WZ(X(1)+1,Y(1)-2))=0 THEN GOSUB 2900:RETURN
7300 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7310 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2910:GOSUB 2920:RETURN
7320 IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2920:E=0:RETURN
7330 GOSUB 2910:E=0:RETURN
7350 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2920:RETURN
7360 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2910:RETURN
7380 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2910:GOSUB 2910:E=0:RETURN
7390 IF Y(1)>1 THEN IF WZ(X(1)-1,Y(1)-2)=0 THEN GOSUB 2920:GOSUB 2920:E=0:RETURN
7395 E=0:RETURN
7400 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2920:GOSUB 2910:E=1:RETURN
7410 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2910:GOSUB 2910:E=1:RETURN
7420 IF WZ(X(1)-2,Y(1))=0 THEN GOSUB 2920:RETURN
7430 IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2910:RETURN
7450 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7460 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2930:GOSUB 2910:RETURN
7470 IF WZ(X(1)-1,Y(1)-2)=0 THEN GOSUB 2930:E=0:RETURN
7480 IF WZ(X(1),Y(1)-4)=0 THEN GOSUB 2910:E=0:RETURN
7490 E=0:RETURN
7500 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2930:RETURN
7510 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2910:RETURN
7530 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)-2)=0 THEN GOSUB 2910:GOSUB 2910:E=0:RETURN
7540 IF Y(1)<19 THEN IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2930:GOSUB 2930:E=0:RETURN
7545 E=0:RETURN
7550 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2930:GOSUB 2910:E=1:RETURN
7560 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2910:GOSUB 2910:E=1:RETURN
7570 IF WZ(X(1)-2,Y(1))=0 THEN GOSUB 2930:RETURN
7580 IF WZ(X(1)-1,Y(1-2))=0 THEN GOSUB 2910:RETURN
7600 IF WZ(X(S),Y(S)-4)=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7610 IF WZ(X(S)-1,Y(S)-2)=0 THEN GOSUB 2920:GOSUB 2900:RETURN
7620 IF WZ(X(S),Y(S)-2)=0 THEN GOSUB 2920:RETURN
7630 GOSUB 2900:RETURN
7650 IF WZ(X(S)-1,Y(S)-2)=0 THEN GOSUB 2920:GOSUB 2900:RETURN
7660 IF Y(S)>3 THEN IF WZ(X(S),Y(S)-4)=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7670 IF WZ(X(S)-2,Y(S))=0 THEN GOSUB 2900:GOSUB 2900:RETURN
7680 I=RND(1):IF I>0.5 THEN 7690
7685 GOSUB 2900:RETURN
7690 GOSUB 2920:RETURN
7700 IF WZ(X(S),Y(S)+4)=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7710 IF WZ(X(S)-1,Y(S)+2)=0 THEN GOSUB 2930:GOSUB 2900:RETURN
7720 IF WZ(X(S),Y(S)+2)=0 THEN GOSUB 2930:RETURN
7730 GOSUB 2900:RETURN
7750 IF WZ(X(S)-1,Y(S)+2)=0 THEN GOSUB 2930:GOSUB 2900:RETURN
7760 IF Y(S)<17 THEN IF WZ(X(S),Y(S)+4)=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7770 IF WZ(X(S)-2,Y(S))=0 THEN GOSUB 2900:GOSUB 2900:RETURN
7780 I=RND(1):IF I>0.5 THEN 7790
7785 GOSUB 2930:RETURN
7790 GOSUB 2900:RETURN
7800 IF WZ(X(S),Y(S)-4)=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7810 IF WZ(X(S)+1,Y(S)-2)=0 THEN GOSUB 2920:GOSUB 2910:RETURN
7820 IF WZ(X(S),Y(S)-2)=0 THEN GOSUB 2920:RETURN
7830 IF WZ(X(S)+1,Y(S))=0 THEN GOSUB 2910:RETURN
7850 IF WZ(X(S)+1,Y(S)-2)=0 THEN GOSUB 2920:GOSUB 2910:RETURN
7860 IF Y(S)>3 THEN IF WZ(X(S),Y(S)-4)=0 THEN GOSUB 2920:GOSUB 2920:RETURN
7870 IF WZ(X(S)+2,Y(S))=0 THEN GOSUB 2910:GOSUB 2910:RETURN
7880 I=RND(1):IF I>0.5 THEN 7890
7885 GOSUB 2910:RETURN
7890 GOSUB 2920:RETURN
7900 IF WZ(X(S),Y(S)+4)=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7910 IF WZ(X(S)+1,Y(S)+2)=0 THEN GOSUB 2930:GOSUB 2910:RETURN
7920 IF WZ(X(S),Y(S)+2)=0 THEN GOSUB 2930:RETURN
7930 IF WZ(X(S)+1,Y(S))=0 THEN GOSUB 2910:RETURN
7950 IF WZ(X(S)+1,Y(S)+2)=0 THEN GOSUB 2930:GOSUB 2910:RETURN
7960 IF Y(S)<17 THEN IF WZ(X(S),Y(S)+4)=0 THEN GOSUB 2930:GOSUB 2930:RETURN
7970 IF WZ(X(S)+2,Y(S))=0 THEN GOSUB 2910:GOSUB 2910:RETURN
7980 I=RND(1):IF I>0.5 THEN 7990
7985 GOSUB 2910:RETURN
7990 GOSUB 2930:RETURN
8000 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2900:E=1:RETURN
8010 IF Y(1)<19 THEN IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2900:GOSUB 2930:RETURN
8020 IF Y(1)>1 THEN IF WZ(X(1)+1,Y(1)-2)=0 THEN GOSUB 2900:GOSUB 2920:RETURN
8030 IF Y(1)<19 THEN IF WZ(X(1)+2,Y(1)+2)=0 THEN GOSUB 2930:RETURN
8040 IF Y(1)>1 THEN IF WZ(X(1)+2,Y(1)-2)=0 THEN GOSUB 2920:RETURN
8045 E=0:RETURN
8050 IF WZ(X(1)+1,Y(1))=0 THEN GOSUB 2900:GOSUB 2900:E=1:RETURN
8060 IF WZ(X(1)+2,Y(1))=0 THEN GOSUB 2900:RETURN
8070 IF Y(1)>1 THEN IF WZ(X(1)+2,Y(1)-2)=0 THEN GOSUB 2900:GOSUB 2920:RETURN
8080 IF Y(1)<19 THEN IF WZ(X(1)+2,Y(1)+2)=0 THEN GOSUB 2900:GOSUB 2930:RETURN
8090 E=0:RETURN
8100 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2920:E=1:RETURN
8110 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2920:GOSUB 2900:E=0:RETURN
8120 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)+2)=0 THEN GOSUB 2920:GOSUB 2910:E=0:RETURN
8130 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)+4)=0 THEN GOSUB 2900:E=0:RETURN
8140 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)+4)=0 THEN GOSUB 2910:E=0:RETURN
8145 E=0:RETURN
8150 IF WZ(X(1),Y(1)+2)=0 THEN GOSUB 2920:GOSUB 2920:E=1:RETURN
8160 IF WZ(X(1),Y(1)+4)=0 THEN GOSUB 2920:E=0:RETURN
8170 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)+4)=0 THEN GOSUB 2900:GOSUB 2920:RETURN
8180 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)+4)=0 THEN GOSUB 2910:GOSUB 2920:RETURN
8190 E=0:RETURN
8200 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2930:E=1:RETURN
8210 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)-2)=0 THEN GOSUB 2930:GOSUB 2910:RETURN
8220 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)-2)=0 THEN GOSUB 2930:GOSUB 2900:RETURN
8230 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)-4)=0 THEN GOSUB 2910:E=0:RETURN
8240 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)-4)=0 THEN GOSUB 2900:E=0:RETURN
8245 E=0:RETURN
8250 IF WZ(X(1),Y(1)-2)=0 THEN GOSUB 2930:GOSUB 2930:E=1:RETURN
8260 IF WZ(X(1),Y(1)-4)=0 THEN GOSUB 2930:RETURN
8270 IF X(1)>1 THEN IF WZ(X(1)-1,Y(1)-4)=0 THEN GOSUB 2900:GOSUB 2930:RETURN
8280 IF X(1)<4 THEN IF WZ(X(1)+1,Y(1)-4)=0 THEN GOSUB 2910:GOSUB 2930:RETURN
8290 E=0:RETURN
8300 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2910:E=1:RETURN
8310 IF Y(1)>1 THEN IF WZ(X(1)-1,Y(1)-2)=0 THEN GOSUB 2910:GOSUB 2920:RETURN
8320 IF Y(1)<19 THEN IF WZ(X(1)-1,Y(1)+2)=0 THEN GOSUB 2910:GOSUB 2930:RETURN
8330 IF Y(1)>1 THEN IF WZ(X(1)-2,Y(1)-2)=0 THEN GOSUB 2920:RETURN
8340 IF Y(1)<19 THEN IF WZ(X(1)-2,Y(1)+2)=0 THEN GOSUB 2930:RETURN
8345 E=0:RETURN
8350 IF WZ(X(1)-1,Y(1))=0 THEN GOSUB 2910:GOSUB 2910:E=1:RETURN
8360 IF WZ(X(1)-2,Y(1))=0 THEN GOSUB 2910:RETURN
8370 IF Y(1)>1 THEN IF WZ(X(1)-2,Y(1)-2)=0 THEN GOSUB 2910:GOSUB 2920:RETURN
8380 IF Y(1)<19 THEN IF WZ(X(1)-2,Y(1)+2)=0 THEN GOSUB 2910:GOSUB 2930:RETURN
8390 E=0:RETURN
8400 IF X(1)-X(S)=0 THEN 8450
8410 I=Y(1)*8:J=X(1)*16-16: FOR III=-1 TO 0
8415 FOR II=0 TO 7:LINE I+II,J,I+II,J+15,III^2:LINE I-II,J,I-II,J+15,III^2
8420 FOR JJ=1 TO 100:NEXT :NEXT :NEXT : GOTO 2940
8450 I=Y(1)*8-8:J=X(1)*16-8: FOR III=-1 TO 0
8455 FOR II=0 TO 7:LINE I,J+II,I+15,J+II,III*III:LINE I,J-II,I+15,J-II,III*III
8470 FOR JJ=1 TO 100:NEXT :NEXT :NEXT : GOTO 2940
8500 IF WZ(X(S),Y(S)+4)=0 THEN GOSUB 2930:GOSUB 2930:RETURN
8510 IF WZ(X(S),Y(S)+2)=0 THEN GOSUB 2930:RETURN ELSE RETURN
8600 IF WZ(X(S),Y(S)-4)=0 THEN GOSUB 2920:GOSUB 2920:E=0:RETURN
8610 IF WZ(X(S),Y(S)-2)=0 THEN GOSUB 2920:RETURN ELSE RETURN
8800 IF E=1 THEN 8400 ELSE 2050
9000 CLS:PRINT "①存档一","②存档二","③存档三","④存档四","⑤存档五";
9011 T$=INKEY$:IF (T$="b")+(T$="n")+(T$="m")+(T$="g")+(T$="h") THEN 9012 ELSE BEEP:9011
9012 GOSUB 10:LOCATE T,9:PRINT "保存中…";
9013 CLOSE #1:OPEN "DJFMJsave"+STR$(T) FOR OUTPUTAS#1
9020 WRITE #1,A,B,X,Y,MO,DJ,HP,HPP,MP,MPP,JY,JYZ,GGL,Q,R,M(1),N(1),QT,W,V
9040 FOR I=2 TO 6:WRITE #1,KP(I),KPP(I):NEXT
9050 FOR I=1 TO 6:WRITE #1,YPS(I):NEXT
9060 FOR I=1 TO 5:WRITE #1,X(I),Y(I),WZ(X(I),Y(I)):NEXT :CLOSE #1:GOSUB 9175:RETURN
9110 CLS:PRINT "①存档一","②存档二","③存档三","④存档四","⑤存档五";
9111 T$=INKEY$:IF (T$="b")+(T$="n")+(T$="m")+(T$="g")+(T$="h") THEN 9112 ELSE BEEP:9111
9112 GOSUB 10:LOCATE T,9:PRINT "载入中…";
9113 OPEN "DJFMJsave"+STR$(T) FOR INPUT AS#1
9120 INPUT #1,A,B,X,Y,MO,DJ,HP,HPP,MP,MPP,JY,JYZ,GGL,Q,R,M(1),N(1),QT,W,V
9140 FOR I=2 TO 6:INPUT #1,KP(I),KPP(I):NEXT
9150 FOR I=1 TO 6:INPUT #1,YPS(I):NEXT
9160 FOR I=1 TO 5:INPUT #1,X(I),Y(I),WZ(X(I),Y(I)):NEXT
9170 CLOSE #1:GOSUB 9175: GOTO 9180
9175 OPEN "FMYXZMAP" FOR RANDOM AS #1 LEN=128:FIELD #1,128 AS MAP$:RETURN
9180 RR=0:IF B=1 THEN 505 ELSE CLS:GOTO 2050
9200 CLS:PRINT "本程序由薛顺健开发设计,本着要把游戏做得更好的原则,但我的时间和精";
9210 PRINT "力有限,还望各位用户见谅!";:T$=INKEY$: GOTO 9300
9300 GET #1,56:CLS:PRINT MAP$;:GOSUB 9994
9320 T$=INKEY$:GOSUB 10
9325 IF T<1 OR T>6 THEN 9320
9330 I=1:ON T GOTO 9350,9360,9390,9500,9200,9800
9340 BEEP: GOTO 9320
9350 RETURN
9360 P=1:CLS:PRINT "*****";NA$(1);"的状态";"*****";: GOTO 9370
9365 T=ASC(INKEY$):IF T=20 OR T=21 OR T=27 THEN 9366 ELSE BEEP:9365
9366 IF T=20 THEN P=P-1:IF P=0 THEN BEEP:P=1:9365
9367 IF T=21 THEN P=P+1:IF P=3 THEN BEEP:P=2:9365
9368 IF T=27 AND I=1 THEN 9300 ELSE IF T=27 AND I=0 THEN 9900
9369 ON P GOTO 9370,9380
9370 LOCATE 2,1:PRINT SPC (69);
9371 LOCATE 2,1:PRINT "等级:";DJ;"级","经验:";JY;"/";JYZ,"体力:";HP;"/";HPP;
9375 GOTO 9365
9380 LOCATE 2,1:PRINT SPC (69);
9381 LOCATE 2,1:PRINT "法力:";MP;"/";MPP,"攻击力:";GGL,"金钱:";MO;
9385 GOTO 9365
9390 CLS:PRINT "①存档","②读档"
9391 T$=INKEY$:IF T$="b" OR T$="n" OR ASC(T$)=27 THEN 9392 ELSE BEEP:9391
9392 GOSUB 10:IF T=1 THEN GOSUB 9000: GOTO 9300
9393 IF T=2 THEN CLOSE #1: GOTO 9110
9394 IF ASC(T$)=27 THEN 9300
9400 CLS:PRINT "①";YP$(1);"×";:LOCATE 1,11:PRINT "④";YP$(4);"×";
9401 LOCATE 2,1:PRINT "②";YP$(2);"×";:LOCATE 2,11:PRINT "⑤";YP$(5);"×";
9402 LOCATE 3,1:PRINT "③";YP$(3);"×";:LOCATE 3,11:PRINT "⑥";YP$(6);"×";
9403 LOCATE 4,15:PRINT RW$(1);
9410 LOCATE 1,9:PRINT YPS(1);:LOCATE 1,19:PRINT YPS(4);
9411 LOCATE 2,9:PRINT YPS(2);:LOCATE 2,19:PRINT YPS(5);
9412 LOCATE 3,9:PRINT YPS(3);:LOCATE 3,19:PRINT YPS(6);
9415 T$=INKEY$:GOSUB 10
9416 IF T$=CHR$(27) THEN S=1:9490
9417 GXX(T)=GX(T):GXX(3)=HPP-HP:GXX(6)=MPP-MP
9418 IF T<4 AND HPP9419 IF T>3 AND MPP9420 IF YPS(T)=0 THEN BEEP:9415
9421 IF T>3 AND MP=MPP THEN LOCATE 5,1:PRINT AAA$(5);:BEEP:9415
9422 IF T<4 AND HP=HPP THEN LOCATE 5,1:PRINT AAA$(4);:BEEP:9415
9430 IF T>3 THEN PR$="法力+":MP=MP+GXX(T) ELSE PR$="体力+":HP=HP+GXX(T)
9460 YPS(T)=YPS(T)-1: FOR II=1 TO 0 STEP -1: FOR I=0 TO 9:CIRCLE 120,56,I,1,II
9470 FOR J=1 TO 100:NEXT :NEXT :NEXT :LOCATE 4,15:PRINT RW$(1);
9480 LOCATE 5,1:PRINT PR$;GXX(T);:T$=INKEY$:T$="Y"
9490 GOSUB 9994:RETURN
9500 GOSUB 9400: GOTO 9300
9800 CLS:PRINT " 程序设计:薛顺健 E-mail:wqstar028@mai l.china.com"
9810 PRINT " QQ:12068232 有空请和我联系!";
9820 T$=INKEY$:BOX 0,0,160,80,1,2:FOR I=1 TO 2000:NEXT:FOR I=0 TO 80
9821 BOX I,I/2,160-I,80-I/2,0,0:FOR J=1 TO 50 :NEXT:NEXT
9825 CLS:LOCATE 2,7:PRINT "谢谢使用!":LOCATE 3,9:PRINT "再见!"
9830 FOR I=1 TO 4000:NEXT:POKE 199,155
9900 GET #1,55:CLS:PRINT MAP$;:LOCATE 5,16:PRINT " ";:I=0
9910 T$=INKEY$:GOSUB 10:ON T GOTO 9920,9360,9930,9800:BEEP: GOTO 9910
9920 GOSUB 9994:RETURN
9930 GOSUB 9000: GOTO 9900
9994 LOCATE 5,1:PRINT " ";:RETURN
9995 GET #1,R+1
9996 LOCATE 1,1:PRINT MAP$;:LOCATE X(1),Y(1):PRINT RW$(1);: FOR J=2 TO 5:IF KP(J)=0 THEN 9998
9997 LOCATE X(J),Y(J):PRINT NPC$(J);
9998 NEXT :IF R=INT(R/5)*5 THEN WZ(3,19)=6
9999 RETURN


说到RPG的话,BBK上有个完成度还行的
http://kfxsmxd.5d6d.com/bbs.php

想怀旧的话,还有古物Apple][,不过自己是没有经历到那么久远的事情。
SuperCard和QBasic/VB6是什么已经没有人在意了。

不过现在这年头,拿Scratch或者PyGame或者Processing都不算复杂的事情,还天生跨平台。

不过多少这些也只是小群体的自娱自乐罢了。

说到RPG的话,当行为和氛围一致的时候是最有感觉的。哪怕仅仅是路过一片风景,也有其中蕴含的情感。 一路上考虑人物的培养和行动的选择与策略以及人物之间内心和态度的发展。
----
好吧,我想到 FF7/10 和 sola 了,当然风格和类型是不限于此了。