Posts for Bisqwit


Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
alden wrote:
So, this movie doesn't show superhuman playing?
It shows flawed playing (by superhuman standards).
Only because you're now aware of something that can possibly invalidate its flawlessness -- which you could not figure out by just watching. We publish visual performance here. I don't think I can possibly formulate an answer that will satisfy you, Warp -- it's not the first time I catch you in an utterly pedantic debate and even though I know exactly what I mean, I just cannot get it across to you in such a mechanically precise technically correct form which you would understand and accept. You're like a lawyer that is completely ignorant to the spirit of the law, going by the letter of it. Now, Kyrsimys has tried his best -- I don't think I could do better. And I still think he is right and you are not.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Impossible to please everyone, it seems. Ask for rigorous personal testing and proofing, fail. Entrust a co-admin's assertion that the requirements are met, fail.* To adelikat I said, that considering that I'm withdrawing from the site, I will now "err on the side of trust". I don't think that I had any better options. I stand by this PoV. *) The list could go on.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
I say go for it [publication]. If the movie is sloppy, it gives the newcomers (or anyone else) another TAS they can try to beat! :)
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
I got 35 out of 80. My results here: http://bisqwit.iki.fi/kala/snap/mmbossquiz.png
Post subject: Re: Need some CSS assistance
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
Sir VG wrote:
http://www.imamyth.com/speedruns/index.html
It's a really bad idea to load your CSS using JavaScript.
I agree fully with the sentiment and the reasons listed. This is Sir VG's site, as shown on my browser by default.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Shinryuu wrote:
How about this and this
How did your talks with the ceiling cat go?
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Rick wrote:
You just seem to pop up in places I wouldn't expect or think of you to pop up at, I guess. At least to me, I dunno. o.o
Ah. Well, I admit to that accusation. Happens IRL, too, though to a much more localized extent.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Rick wrote:
I think Bisqwit also has a way of sneaking into places where you least expect him.
What makes you say so?
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
DrJones wrote:
This run not only does break the game in half, it also breaks the halves in a mandelbrot fashion. Hell yes! Not satisfied with obsoleting the previous movie by FOUR HOURS, you totally humiliate the rest of the movie by turning it into a mockery of itself, in which empty chairs are put on a trial and trapped characters just pass through the wall!! I think the secret resides on the first four minutes of the run, where you restart constantly to mesmerize us with the pendulus into believing anything you want us to believe.
I rate your post with 9.2 entertainment and an "approved" stamp :)
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Watched the Youtube movie. Thanks Moozooh FODA! Yeah, I'd appreciate subtitles or something explaining what's happening in each menu manipulation step. Other than that, I liked it.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Judging from the comments... This is a movie where the following rhetorical question is appropriate: Who needs strategy when you can just blast yourself through the wafers of the universe? I would have made an AVI, but turns out that snes9x from SVN has all kinds of windowsisms and fails to compile on my Linux 64-bit. Which means, I can't watch it either, until someone else makes an AVI... EDIT: Also, I think the judging decision should be akin to "accept, replace non-newgame+ TAS, welcome a to-be-submitted new category where [maybe: no bosses are skipped, and] all sidequests are completed".
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
Bisqwit wrote:
Well, if anyone comes to meet me personally, don't expect anything spectacular -- just some chat and possibly coffee and cake if I get sufficient warning in advance :)
"Cake and grief counseling will be available at the conclusion of the test" ?-)
Right ;)
Post subject: Re: Small Article / Top Ten TAS List
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
alden wrote:
Google alerted me to this: http://www.crispygamer.com/features/2009-04-20/as-fast-as-impossible-10-insanely-thrilling-toolassisted-speedruns.aspx
Nice find, nice article! I rate 9.7 8.4 in entertainment and 6.2 in technical. EDIT: In afterthought, 9.7 is a bit inflated.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Well, if anyone comes to meet me personally, don't expect anything spectacular -- just some chat and possibly coffee and cake if I get sufficient warning in advance :)
Post subject: Image/media server API written
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
The API for the image/media server has now been written. The main site accesses the image/media server exclusively through the API, meaning that the image/media server can now be migrated at relative ease when someone offers to host it. Who will? It currently weighs 463 MB, with the bulk of that room being taken by site snapshots and various short demonstration AVIs I have occasionally linked to on these forums.
Post subject: Re: The Nesvideos meet
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Guybrush wrote:
Ack, I'm not good at writing these... I'm arranging a Nesvideos meet that will take place in Turku, Finland. Point of time when this meet will happen isn't sure yet and it depends on many factors like: When I have time, when others have time and such. But I think it should be done next month.
Which next month is that? :P Well anyway if anyone of you foreigners (whom I have talked with at e.g. IRC) visits Finland at any time, please notify me, I can possibly arrange a meeting.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Xkeeper wrote:
it both allows loops and creates inaccessabilities, even though that really isn't intentional...
Well, fix that first :) Those are default features that are usually produced by naive/buggy maze generation algorithms; I only included them in the list because for introducing them in that C++ code requires a bit of understanding how it works.
Post subject: Artistic programming
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
Rather than posting such long source codes directly, would it be too much of trouble putting the source code somewhere else and posting a link?
The reason I post the code here is because I think that the code is cool and interesting to read for a reason or other*. If the only interesting thing is the end result and the how it runs, I'd rather just link to it somewhere and probably not even post here. At least I try to keep the source code short enough to work nicely as a part of a posting. Though my last two [in the C thread] somewhat offend this goal, hopefully offsetting that offense with awesome… ------- *) Your mileage may vary. I'm a supporter of an artistic programming advocates organization in Finland and I advocate code that is interesting to read, and in general, recommend programmers to read more than they write. That is one of the reasons why I hate code that looks like labor: pick up a brush. locate paint can. open can lid. A: put brush in can. pull brush. look at wall. B: locate section you want to paint. place brush on section. drag brush on wall. if X then goto B. if Y then goto A. Lots of statements, lots of full stops, lots of repetition, very little tangible progress achieved. The general guideline of artistic programs is that the more you get to express what you want accomplished and the less you need to express what to do, the better the artistic value of the source code text. Focus on the is verb rather than on the do verb. Functional programming lends itself for this point of view easily. Layout of the code can improve readability a lot, though it should not happen at the expense of being able to follow what the program does. (For example, the layout of this program strongly suggests that it plays Othello, but it's anything but readable.)
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
LagDotCom wrote:
All the examples you give are aspects of magnitude; that is, the eyes/heads are too big, etc. Whereas 'drawing eyes on top of hair' is just plain wrong, rather than a caricature. What sayeth thou?
But both are a matter of emphasis.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Wow. No submissions whatsoever :o Well, the challenge is closed now. Derakon gets a cookie for trying.
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Kuwaga wrote:
http://en.wikipedia.org/wiki/Mudra Karana mudrā
Oh, that's where the "hand seals" of the Naruto series are from :o
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
This, too, is for QuickBASIC. Probably the most compact Tetris clone I've ever written, in terms of amount of code, in any language.
DEFINT a-z
SCREEN 0,1,0,0 : WIDTH 40,25: COLOR 2,0: CLS
DIM SHARED area(23), blockdata(3,7)

' Read block data (each block is a 4x4 bitmask, 16 bits)
FOR bn=0 TO 3: FOR br=1 TO 7: READ blockdata(bn,br): NEXT br,bn
DATA &H2222,&H0270,&H1320,&H4620,&H0E20,&H02E0,&H0660 :REM rotation 0
DATA &H0F00,&H4640,&H6300,&H3600,&H2260,&H2230,&H0660 :REM rotation 1
DATA &H2222,&H7200,&H1320,&H4620,&H4700,&H0740,&H0660 :REM rotation 2
DATA &H0F00,&H1310,&H6300,&H3600,&H3220,&H6220,&H0660 :REM rotation 3
'             x      xx      xx    xxx      x    xx
'     xxxxx  xxx      xx    xx       x    xxx    xx

' Generate field borders (each area row is a bitmask)
FOR ty=0 TO 23: area(ty) = &H7FFF: NEXT  '.XXXXXXXXXXXXXXX at top& bottom
FOR ty=1 TO 19: area(ty) = &H6007: NEXT  '.XX..........XXX at edges
CALL RenderField(0,23) 'render the entire field

' FNbitvalue& = 1 << bitno
' FNbittest = value & FNbitvalue&
' FNblockbit = gives the [by][bx] of block bn(1-7) in rotation br(0-3)
' FNblockrow = gives the [by][0-3] of block, shifted right by bx (bx=area horizontal pos)
DEF FNbitvalue&(bitno) = 2& ^ bitno 'Declared as LONG (&) so that it works in QBASIC too
DEF FNbittest(value, bitno) = SGN(value AND FNbitvalue&(bitno))
DEF FNblockbit(br,bn,bx,by) = FNbittest(blockdata(br,bn), by * 4 + bx) ' decode block matrix
DEF FNblockrow(br,bn,bx,by) = ((blockdata(br,bn) \ FNbitvalue&(by * 4)) AND 15) * FNbitvalue&(bx)

'Generate next block
nr = 0: nn = 1 + INT(RND * 7) 'generate new next block
COLOR 12,0: LOCATE 1, 15 + 4: PRINT "Next block:"
FallingSpeed! = 0.4   '0.4s per round
DO
  CALL RenderBlock(nr,nn, nx+15,ny+1, 0) 'unrender old next block
  pr = nr: pn = nn : px = 4 : py = 0     'next block becomes incoming block
  nr =  0: nn = 1 + INT(RND * 7)         'generate new next block
  CALL RenderBlock(nr,nn, nx+15,ny+1, 1) 'render next block
  CALL RenderBlock(pr,pn, px,py, 1)      'render incoming block
  IF TestCollision(pr,pn, px,py) THEN EXIT DO 'game over if the block collided upon birth

  ti! = TIMER ' Beginning of this round
  FallingDown = 0
HandleInput:
  DO
    qx=px: qy=py: qr=pr ' Configure the position where block will be moved
    y$ = INKEY$
    IF y$ <> "" THEN FallingDown = 0
    IF y$ = CHR$(27) THEN GOTO GameOver
    IF y$ = CHR$(0) + "H" THEN qr = (qr+1)MOD 4: GOTO TestMove 'test rotate
    IF y$ = CHR$(0) + "K" THEN qx = qx-1:        GOTO TestMove 'test move left
    IF y$ = CHR$(0) + "M" THEN qx = qx+1:        GOTO TestMove 'test move right
    IF y$ = CHR$(0) + "P" THEN EXIT DO 'skip round (move down)
    IF y$ = " " THEN FallingDown = 1
  LOOP UNTIL FallingDown OR (TIMER >= ti! + FallingSpeed!)

MoveDown:
  qy = qy+1
  IF TestCollision(qr,pn, qx,qy) THEN 'Can the block move down?
    CALL AffixBlock(pr,pn, px,py)     'Nope. Affix it here.
    CALL RenderField(py, py+3)        'Render the block.
    NumLinesRemoved = VerifyFullLines 'When you add scoring, handle this
    'Continue to the next loop
  ELSE
    ti! = TIMER ' Begin new round
    GOTO BlockMoved
TestMove:
    IF TestCollision(qr,pn, qx,qy) THEN GOTO HandleInput ' ignore if couldn't move
BlockMoved:
    CALL RenderBlock(pr,pn, px,py, 0) 'unrender from old position
    pr=qr: px=qx: py=qy               'change block location and orientation
    CALL RenderBlock(pr,pn, px,py, 1) 'render into new position
    GOTO HandleInput
  END IF
LOOP

GameOver:
LOCATE 1,1: COLOR 12,4: PRINT "GAME OVER"
END

FUNCTION TestCollision(rotation,blocknum, bx,by)
  FOR y = 0 TO 3
    IF y+by > 0 AND (area(y+by) AND FNblockrow(rotation,blocknum, bx,y)) THEN EXIT FOR
  NEXT
  TestCollision = y < 4 ' Was collision if the loop was interrupted.
END FUNCTION

SUB AffixBlock(rotation,blocknum, bx,by)
  FOR y = 0 TO 3
    area(y+by) = area(y+by) OR FNblockrow(rotation,blocknum, bx,y)
  NEXT
END SUB

SUB RenderField(miny, maxy)
  FOR y = miny TO maxy
    LOCATE y + 1,1
    FOR x = 1 TO 14
      IF area(y) AND FNbitvalue&(x) THEN COLOR 7,7 ELSE COLOR 4,0
      PRINT ".";
  NEXT x, y
END SUB

SUB RenderBlock(rotation,blocknum, px,py, showorhide)
  IF showorhide=0 THEN COLOR 4,0 ELSE COLOR 11,3
  FOR y = 0 TO 3: FOR x = 0 TO 3
    IF y + py > 0 AND FNblockbit(rotation,blocknum,x,y) THEN
      LOCATE py+y+1, px+x: PRINT ".";
    END IF
  NEXT x, y
END SUB

FUNCTION VerifyFullLines()
  numlinesremoved = 0
  FOR y = 19 TO 1 STEP -1
    WHILE area(y) = &H7FFF 'full line?
      FOR py = y TO 2 STEP -1: area(py) = area(py-1): NEXT 'move down
      area(1) = &H6007 'the top row becomes blank (.XX..........XXX)
      CALL RenderField(1,y) 're-render the changed part of the field
      numlinesremoved = numlinesremoved + 1
    WEND
  NEXT
  VerifyFullLines = numlinesremoved
END SUB
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Alas, no grue there. The game is already too difficult :)
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
I wrote a little adventure game in C++.
#include <string>
#include <utility>
#include <deque>
#include <cstdlib>
#include <cstdarg>
#include <iostream>
#include <cmath>
#include <cstdio>
#include <map>

static const struct { const char* a; float b, c; } MoneyTypes[] =
  { {"platinum",10,0}, {"gold",1,0}, {"silver",.6},
    {"bronze",.4,0}, {"copper",.2,0}, {"wood",.01} },
  BuildTypes[] =
  { {"iron",.4,3}, {"fur",.01,.2}, {"platinum",2,4}, {"gold",1,3.5},
    {"chromium",.9,2}, {"bronze",.1,2.7}, {"pewter",.05,2},
    {"bamboo",.01,1}, {"silk",.01,.1},  {"steel",.7,3},
    {"leather",.09,.5}, {"glass",.04,2} },
  ItemTypes[] =
  { {"shirt",1}, {"tie",.25}, {"bracelet",.2}, {"shoe",.4}, {"leggings",.8},
    {"battlesuit",10}, {"hammer",.4}, {"dagger",.1}, {"cap", .6},
    {"crown",3}, {"sceptre",4}, {"cape",.7}, {"overalls",4} },
  CondTypes[] =
  { {"awesome",1.2}, {"excellent",1}, {"good",.9}, {"average",.75},
    {"bad",.6}, {"poor",.5}, {"thrashed",.4} },
  WallTypes[] = { {"open",1}, {"collapsed",.5}, {"blocked"} },
  EnvTypes[] = { {"dark"}, {"humid"}, {"tall"}, {"beautiful"}, {"narrow"} },
  BodyParts[] = { {"finger",10}, {"teeth",30}, {"elbow",100}, {"toe",40} },
  FoodTypes[] = {
    {"b akbdl epqfts dblf",50000}, {"b kbqhf okbsf pe dgjdlfm kfht",35000},
    {"b dbvkcqpm pe dpplfc opsbspft",20000}, {"b dgjdlfm gps cph",10000},
    {"dgfftf bmc nbdbqpmj",6000}, {"b avssfqnjkl ajtdvjs",3000}, {"b apjkfc fhh",2000},
    {"tpnf kjdgfm tsfx",1000}, {"b xppc dpqsfw tbmcxjdg",700},
    {"b dvo pe ujmfhbq", 500}, {"b dvo pe bookf tffct", 300},
    {"b qpssfm dbqqps", 200}, {"b nvh pe nvccz xbsfq",110}, {"tpnf qbaajs cqpoojmht",70},
    {"b cfbc dpdlqpbdg", 50}, {"b npmsg pkc tojcfq mfs", 30},
    {"b gjkk pe cvts", 16}, {"b gfbo pe cvts", 8}, {"b ajh ojkf pe cvts", 4},
    {"b ojkf pe cvts", 2}, {"b tofdlkf pe cvts", 1}
  }; // badcfehgjilknmporqtsvuxwzy

#define NItems(x) (sizeof((x))/sizeof(*(x)))
static double frand() { return std::rand() / ((double)RAND_MAX+1.0); }
static const char* Sep(size_t pos, size_t max) { return pos?pos==max-1?" and ":", ":""; }

struct ItemType {
    size_t build, item, cond; // Indexes.
    const std::string Name1() const { return ItemTypes[item].a; }
    const std::string Name2() const { return std::string(BuildTypes[build].a)+" "+Name1(); }
    const std::string Name3() const { return std::string(CondTypes[cond].a)+" "+Name2(); }
    const std::string Name1c() const { return std::string(CondTypes[cond].a)+" "+Name1(); }
    const std::string Name3b(bool condition = false) const {
        std::string n = condition ? Name3() : Name2();
        if(n[n.size()-1]=='s') return n;
        return (n[0]=='a'||n[0]=='e'||n[0]=='i'||n[0]=='o'||n[0]=='u' ? "an ":"a ")+n;
    }
    const std::string Name(int level) const
        { return level==1 ? Name1() : level==2 ? Name2() : level==3 ? Name3() : Name1c(); }
    ItemType()
        : build ( std::rand() % (frand() > 0.4 ? NItems(BuildTypes) : 2) ),
          item ( std::rand() % (frand() > 0.4 ? NItems(ItemTypes) : 4) ),
          cond ( std::rand() % (frand() > 0.8 ? NItems(CondTypes) : 3) ) { }
    double value() const {
        return 200.0 * ItemTypes[item].b * BuildTypes[build].b * CondTypes[cond].b;
    }
    double weight() const { return BuildTypes[build].c; }
};
static std::string Appraise(double value,int v=1,int maxi=3) {
    std::deque<std::string> list; redo:
    for(size_t a=0; a<NItems(FoodTypes); ++a)
        if(value >= FoodTypes[a].b*3.0) {
            std::string k = FoodTypes[a].a;
            for(size_t b=0; b<k.size(); ++b) if(k[b]-' ') k[b]=1+((k[b]-1)^v);
            list.push_back(k); value -= FoodTypes[a].b*3.0;
            if((int)list.size() < maxi) goto redo; break;
        }
    if(list.empty()) list.push_back("only hungry dreams");
    std::string resp;
    for(size_t a=0; a<list.size(); ++a) resp += Sep(a,list.size()) + list[a];
    return resp;
}
static struct Eq {
    std::deque<ItemType> Items;
    long Money[ NItems(MoneyTypes) ];
    
    template<typename PrFunc>
    bool print(bool is_inv, PrFunc& P) const {
        double moneyvalue = 0;
        for(size_t a=0; a<Items.size(); ++a)
            P("%s\n", Items[a].Name3b(!is_inv).c_str()),
            moneyvalue += Items[a].value();
        if(is_inv && moneyvalue) P("The total value of your items is %.2f gold.\n", moneyvalue);
        moneyvalue = 0;
        for(size_t a=0; a<NItems(Money); ++a)
            if(Money[a])
                P("%ld %s coin%s\n",
                    Money[a], MoneyTypes[a].a, "s"+(Money[a]==1)),
                moneyvalue += Money[a] * MoneyTypes[a].b;
        if(is_inv && moneyvalue) P("The coins are worth %.2f gold total\n", moneyvalue);
        if(is_inv)
        {
            P("Your possessions wear you down %d points every step you take.\n", burden());
            P("You estimate that these possessions could earn you %s.\n", Appraise(value()).c_str());
        }
        return moneyvalue > 0 || !Items.empty();
    }
    double value() const {
        double res = 0;
        for(size_t a=0; a<Items.size(); ++a) res += Items[a].value();
        for(size_t a=0; a<NItems(Money); ++a) res += Money[a] * MoneyTypes[a].b;
        return res;
    }
    int burden() const {
        double weight = 0;
        for(size_t a=0; a<NItems(Money); ++a) weight += Money[a] / 100.0;
        for(size_t a=0; a<Items.size(); ++a) weight += Items[a].weight();
        return 1 + weight;
    }
    double look_item(long no, bool in, bool all) const {
        if(all) std::printf("You see a %s made of %s, in %s condition. ",
            ItemTypes[Items[no].item].a,
            BuildTypes[Items[no].build].a,
            CondTypes[Items[no].cond].a);
        else std::printf("It is a %s made of %s. It is in %s condition.\n",
            ItemTypes[Items[no].item].a,
            BuildTypes[Items[no].build].a,
            CondTypes[Items[no].cond].a);
        if(in) std::printf("It does not hide anything interesting inside thereof.\n");
        else if(all) std::printf("\n");
        else std::printf(
            "You estimate that with it you could probably buy %s.\n",
            Appraise(Items[no].value(),1, 1).c_str());
        return Items[no].value();
    }
    void look_money(long a, bool in, bool all) const {
        std::printf("%ld %s coin%s\n", Money[a], MoneyTypes[a].a, "s"+(Money[a]==1));
        if(in) std::printf("They are just normal coins.\n");
        else if(!all) std::printf("The coins are worth %.2f gold total\n", Money[a]*MoneyTypes[a].b);
    }
    void clear(size_t n_items = 0) {
        Items.resize(n_items);
        for(size_t a=0; a<NItems(Money); ++a) Money[a] = 0;
    }
    std::deque<std::string> move(Eq& target, const std::string& what, bool all) {
        std::deque<std::string> movelist;
        Eq target_backup = target, me_backup = *this;
        bool ok = true;
        if(all)
            ok = move_one(target, what, movelist, all);
        else
            for(size_t begin = 0; begin != what.size(); ) {
                while(what[begin]==' ') ++begin;
                size_t end = what.find(',', begin);
                if(end == what.npos) end = what.size();
                size_t e = end;
                while(e > begin && what[e-1] == ' ') --e;
                ok = ok && move_one(target, what.substr(begin, e-begin),
                                    movelist, all);
                begin = end; if(begin<what.size()&&what[begin]==',') ++begin;
            }
        if(!ok) movelist.clear();
        if(movelist.empty()) {
            std::printf("Nothing moved!\n");
            target = target_backup;
            *this = me_backup;
        }
        return movelist;
    }
    long find_item(const std::string& what, bool all, size_t first=0) const {
        const char* number_begin = what.c_str() + what.size();
        while(number_begin > what.c_str()
          &&  number_begin[-1] >= '0' && number_begin[-1] <= '9') --number_begin;
        long index = std::strtol(number_begin, 0, 10);
        while(number_begin > what.c_str() && number_begin[-1] == ' ') --number_begin;
        std::string itemname = what.substr(0, number_begin-what.c_str());
        bool skip = itemname.size() != what.size();
        for(int level=4; level>=1; --level)
        {
            long occurrences = 0;
            for(size_t a=first; a<Items.size(); ++a)
                if((itemname == "" && all)
                || itemname == "a "+Items[a].Name(level)
                || itemname == "an "+Items[a].Name(level)
                || itemname == Items[a].Name(level))
                {
                    if(skip && ++occurrences != index) continue;
                    return a;
                }
        }
        return -1;
    }
    long find_money(const std::string& what, bool all, size_t first=0) const {
        for(size_t moneytype = first; moneytype < NItems(MoneyTypes); ++moneytype) {
            if(Money[moneytype] <= 0) continue;
            if((what == "" && all)
            || what == "coins"
            || what == MoneyTypes[moneytype].a
            || what == MoneyTypes[moneytype].a+std::string(" coin")
            || what == MoneyTypes[moneytype].a+std::string(" coins")) {
                return moneytype;
            }
        }
        return -1;
    }
    bool move_one(Eq& target, const std::string& what,
                  std::deque<std::string>& movelist,
                  bool all) {
        bool found_item = false, found_money = false;
        for(long item_id; (item_id=find_item(what, all)) >= 0;) {
            target.Items.push_front(Items[item_id]);
            movelist.push_back(Items[item_id].Name3b());
            Items.erase(Items.begin()+item_id);
            found_item = true;
            if(!all) break;
        }
        char* end = 0, * begin = (char*)what.c_str();
        long numeral = std::strtol(begin, &end, 10);
        if(!end || end == begin) numeral = 0;
        else if(numeral < 1) return found_item;
        for(begin=end; *begin==' '; )++begin;
        std::string moneyname = begin;
        for(long moneytype; (moneytype=find_money(moneyname,all)) >= 0;) {
            long mynumeral = numeral;
            if(mynumeral == 0) mynumeral = Money[moneytype];
            if(mynumeral > Money[moneytype]) return false;
            target.Money[moneytype] += mynumeral;
            Money[moneytype] -= mynumeral;
            char explanation[512];
            std::sprintf(explanation, "%ld %s coin%s",
                mynumeral, MoneyTypes[moneytype].a, "s"+(mynumeral==1));
            movelist.push_back(explanation);
            found_money = true;
            if(!all) break;
        }
        return found_money || found_item;
    }
} eq;
static struct Room {
    size_t Wall, Env; // Indexes
    float Chest;      // 0=no chest
    int seed;         // For maze generation
    Eq items;         // What's lying on the floor
    Room() : Wall(0),Env(0),Chest(0),seed(),items() {}
} const defaultroom;
static struct Maze {
    std::map<long/*x*/,std::map<long/*y*/,Room> > rooms;
    Room& GenerateRoom(long x,long y,const Room& model, int seed) {
        std::srand((x<<12)^y);
        std::pair<std::map<long/*y*/,Room>::iterator, bool>
          insres ( rooms[x].insert(std::make_pair(y, model)) );
        Room& room = insres.first->second;
        if(insres.second) {
            room.Chest = frand() > 0.7 ? 1.0 : 0.0;
            room.seed = (seed + (frand() > 0.9 ? rand() : 0)) & 3;
            if(frand() > 0.9) room.Env = rand() % NItems(EnvTypes);
            if(frand() > (seed==model.seed ? 0.9 : 0.2))
                room.Wall = frand() > 0.9 ? 0 : 2;
            room.items.clear(unsigned(std::pow(frand(),90.0) * 2.5));
        } return room;
    }
    char Char(long x,long y) const
    {
        std::map<long/*x*/,std::map<long/*y*/,Room> >::const_iterator
            i = rooms.find(x);
        if(i == rooms.end()) return ' ';
        std::map<long/*y*/,Room>::const_iterator j = i->second.find(y);
        if(j == i->second.end()) return ' ';
        if(j->second.Wall) return '#';
        if(j->second.Chest > 0.0) return 'c';
        return (j->second.items.value() != 0.0) ? 'i' : '.';
    }
} maze;
#define SpawnRooms(x,y) \
    const Room \
        &room   = maze.GenerateRoom(x,y, defaultroom, 0), \
        &room_n = maze.GenerateRoom(x,y-1, room, 0), \
        &room_s = maze.GenerateRoom(x,y+1, room, 1), \
        &room_w = maze.GenerateRoom(x-1,y, room, 2), \
        &room_e = maze.GenerateRoom(x+1,y, room, 3)
static long x=0, y=0, life=1000;
struct VecPrint: public std::deque<std::string>
{
    void operator() (const char* fmt, ...)
    {
        if(pos < size()) printf("%s | ", operator[](pos++).c_str());
        else if(!empty()) printf("%*s | ", (int)operator[](0).size(), "");
        va_list ap;
        va_start(ap, fmt);
        std::vprintf(fmt, ap);
        va_end(ap);
    }
    size_t pos; VecPrint() : pos(0) { }
    ~VecPrint() /* flush */
    { while(pos < size()) printf("%s |\n", operator[](pos++).c_str()); }
};
static void Look() {
    SpawnRooms(x,y);
    for(int o=1; o<5&&!maze.GenerateRoom(x,y+o, room, 0).Wall; ++o) { SpawnRooms(x,y+o); }
    for(int o=1; o<5&&!maze.GenerateRoom(x,y-o, room, 0).Wall; ++o) { SpawnRooms(x,y-o); }
    for(int o=1; o<6&&!maze.GenerateRoom(x-o,y, room, 0).Wall; ++o) { SpawnRooms(x-o,y); }
    for(int o=1; o<6&&!maze.GenerateRoom(x+o,y, room, 0).Wall; ++o) { SpawnRooms(x+o,y); }
    VecPrint mapgraph;
    for(long yo=-4; yo<=4; ++yo)
    {
        std::string line;
        for(long xo=-5; xo<=5; ++xo)
            line += ((xo==0&&yo==0) ? '@' : maze.Char(x+xo, y+yo));
        mapgraph.push_back(line);
    }
    mapgraph("In a %s tunnel at %+3ld,%+3ld\n", EnvTypes[room.Env].a, x,-y);
    mapgraph("Exits:%s%s%s%s\n",
        room_n.Wall?"":" north",
        room_s.Wall?"":" south",
        room_w.Wall?"":" west",
        room_e.Wall?"":" east");
    if(room.Chest > 0.0 || room.items.value() > 0.0) mapgraph("\n");
    if(room.Chest > 0.0) mapgraph("a chest\n");
    room.items.print(false, mapgraph);
}
static void Inv() {
    if(!eq.print(true, std::printf))
        std::printf("You are carrying nothing.\n");
}
static void LookAt(std::string what, bool in=false) {
    const Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    long no,c=0;
    double value=0;
    if(all)
    {
        for(no=0; (no = room.items.find_item(what,all,no)) >= 0; ++c) value += room.items.look_item(no++,in,all);
        for(no=0; (no = room.items.find_money(what,all,no)) >= 0; ++c) room.items.look_money(no++,in,all);
        if(!c)
        {
          for(no=0; (no = eq.find_item(what,all,no)) >= 0; ++c) value += eq.look_item(no++,in,all);
          for(no=0; (no = eq.find_money(what,all,no)) >= 0; ++c) eq.look_money(no++,in,all);
        }
    }
    else
    {
        for(no=0; (no = eq.find_item(what,all,no)) >= 0; ++c) value += eq.look_item(no++,in,all);
        if(!c) for(no=0; (no = eq.find_money(what,all,no)) >= 0; ++c) eq.look_money(no++,in,all);
        if(!c) for(no=0; (no = room.items.find_item(what,all,no)) >= 0; ++c) value += room.items.look_item(no++,in,all);
        if(!c) for(no=0; (no = room.items.find_money(what,all,no)) >= 0; ++c) room.items.look_money(no++,in,all);
    }
    if(c&&all) std::printf(
        "You estimate that with them you could probably buy %s.\n",
        Appraise(value,1, 1).c_str());
    if(c) return;

    if(what == "" && all && room.Chest > 0)
        std::printf("You see a closed chest here. You can try to <open> it.\n");
    else if(what == "chest" && room.Chest > 0)
        std::printf(in
            ?"The chest is closed. You cannot see inside. You can try to <open> it.\n"
            :"There is a chest here. It is closed. You can try to <open> it.\n");
    else
        std::printf("There %s no %s here that you can look at.\n",
            what[what.size()-1]=='s' ? "are" : "is", what.c_str());
}
static void EatLife(long l) {
    if(life>=800 && life-l<800) std::printf("You are so hungry!\n");
    if(life>=150 && life-l<150) std::printf("You are famished!\n");
    if(life>=70 && life-l<70) std::printf("You are about to collapse any second!\n");
    life -= l;
}
static void OpenChest() {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    if(room.Chest <= 0.0) { std::printf("Open what? There's nothing openable here.\n"); return; }
    std::printf("You try to pry the chest open.\n");
    EatLife(8);
    room.Chest -= 0.1 + std::pow(frand(), 4.0);
    if(frand() > 0.96) {
        unsigned sprain = rand()%NItems(BodyParts);
        EatLife(BodyParts[sprain].b);
        std::printf("You sprain your %s!\n", BodyParts[sprain].a);
    }
    if(room.Chest <= 0.0) {
        std::printf("The chest bursts into pieces!\nEverything it contained is scattered on the ground.\n");
        std::srand((y<<12)^x);
        do
            if(frand() > 0.96) { // pure money is rare.
                unsigned moneytype((1.0-std::pow(frand(), 4)) * (NItems(MoneyTypes)-1));
                room.items.Money[moneytype] += rand() % long(600/MoneyTypes[moneytype].b);
            }
            else
                room.items.Items.push_front(ItemType());
        while(frand() > 0.3);
    }
    else std::printf("The chest resists your meddling! Try harder.\n");
}
static void Get(std::string what) {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    
    if(what == "chest" && room.Chest > 0)
        { std::printf("You cannot take the chest.\n"); return; }
    std::deque<std::string> moved = room.items.move(eq, what, all);
    if(moved.empty())
        std::printf("There %s no %s here!\n",
            what[what.size()-1]=='s'?"are":"is", what.c_str());
    else {
        std::printf("You take ");
        for(size_t a=0; a<moved.size(); ++a)
            std::printf("%s%s", Sep(a,moved.size()), moved[a].c_str());
        std::printf(".\n");
        EatLife(moved.size() * 2);
    }
}
static void Drop(std::string what) {
    Room &room = maze.GenerateRoom(x,y, defaultroom, 0);
    bool all = false;
    if(what == "all") { all=true; what=""; }
    else if(what.substr(0,4) == "all ") { all=true; what.erase(0,4); }
    std::deque<std::string> moved = eq.move(room.items, what, all);
    if(moved.empty())
        std::printf("You don't have %s!\n", what.c_str());
    else {
        std::printf("You drop ");
        for(size_t a=0; a<moved.size(); ++a)
            std::printf("%s%s", Sep(a,moved.size()), moved[a].c_str());
        std::printf(".\n");
        EatLife(moved.size() * 1);
    }
}
static bool TryMove(int xd,int yd) {
    if(maze.GenerateRoom(x+xd, y+yd, defaultroom, 0).Wall)
        { std::printf("You cannot go that way.\n"); return false; }
    x += xd; y += yd; return true;
}
int main() {
    std::string lastcmd, cmd;
    std::printf("Welcome to the death trap.\n\n");
    help: std::printf(
        "Available commands:\n"
        "\tl/look\n\tla/look at <item>\n\tn/s/w/e for moving\n\tget <item>/get all/ga for short,\n"
        "\tdrop <item>/drop all\n\ti/inv/inventory\n\tquit\n\thelp\n\n"
        "You are starving. You are trying to find enough stuff to sell\n"
        "for food before you die. Beware, food is very expensive here.\n\n");
    for(Look(); life > 0; ) {
        std::printf("[life:%ld]> ", life); fflush(stdout);
        std::getline(std::cin, cmd);
        if(!std::cin.good() || cmd == "quit") break;
        if(cmd == "!") cmd = lastcmd; else lastcmd = cmd;
        if(cmd == "i" || cmd == "inv" || cmd == "inventory") Inv();
        else if(cmd == "n" || cmd == "north" || cmd == "go n" || cmd == "go north")
            { if(TryMove(0,-1)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "s" || cmd == "south" || cmd == "go s" || cmd == "go south")
            { if(TryMove(0,1)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "w" || cmd == "west" || cmd == "go w" || cmd == "go west")
            { if(TryMove(-1,0)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "e" || cmd == "east" || cmd == "go e" || cmd == "go east")
            { if(TryMove(1,0)) { EatLife(eq.burden()); Look(); } }
        else if(cmd == "sw" || cmd == "nw" || cmd == "se" || cmd == "ne")
            std::printf("The concept of diagonal movements makes you squeasy.\n");
        else if(cmd.substr(0,4) == "wear" || cmd.substr(0,5) == "wield")
            std::printf("You are scavenging for survival and not playing an RPG character.\n");
        else if(cmd == "l" || cmd == "look")             Look();
        else if(cmd.substr(0,3) == "la ")                LookAt(cmd.substr(3));
        else if(cmd.substr(0,8) == "look at ")           LookAt(cmd.substr(8));
        else if(cmd.substr(0,8) == "look in ")           LookAt(cmd.substr(8),true);
        else if(cmd.substr(0,5) == "look ")              LookAt(cmd.substr(5));
        else if(cmd.substr(0,5) == "l at ")              LookAt(cmd.substr(5));
        else if(cmd.substr(0,5) == "l in ")              LookAt(cmd.substr(5),true);
        else if(cmd.substr(0,2) == "l ")                 LookAt(cmd.substr(2));
        else if(cmd == "open" || cmd == "open chest" || cmd == "pry") OpenChest();
        else if(cmd == "ga")                             Get("all");
        else if(cmd.substr(0,3) == "ga ")                Get("all "+cmd.substr(3));
        else if(cmd.substr(0,4) == "get ")               Get(cmd.substr(4));
        else if(cmd.substr(0,5) == "drop ")              Drop(cmd.substr(5));
        else if(cmd == "help" || cmd == "what" || cmd == "?") goto help;
        else std::printf("what?\n");
    }
    double value = eq.value();
    return std::printf(
        "%s\n[life:%ld] Game over\nYou managed to collect stuff worth %.2f gold.\n"
        "With all your possessions, you purchase %s. You consume your reward eagerly.\nYOU %s\n",
        life<0?"You are pulled out from the maze by a supernatural force!":"byebye",
        life, value,
        Appraise(value).c_str(),
        value<10000.0
            ? "DID NOT SURVIVE. Hint: Learn to judge the value/weight ratio."
            : "SURVIVED! CONGRATULATION. ;)") < 0;
}
A pre-compiled Windows binary can be found in this zip: http://bisqwit.iki.fi/kala/bisqwit-adv.zip In this game you are a starving adventurer whose goal is to scange the dungeon for artifacts worth enough to buy him a meal -- and to do so before he is too famished to do anything. EDIT: Improved the parser a bit, motivated by subtle persuation at TVTropes
Editor, Experienced Forum User, Published Author, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
You could always use the Capcom music engine from Rockman / Rockman 2. It is pretty much plug and play. You can find it at http://bisqwit.iki.fi/jutut/megamansource/ . If you want it in binary format, it's located in rockman2.nes or megaman2.nes at file position $30010. It ends at $30A60, which is immediately followed by a table of song pointers. You'll want to place it so it runs at the address $8000 in your ROM. Each song pointer is 2 bytes. At the address pointed, is the song data. The song data is in this format:
  0000 byte $0F   ; this identifies as a song (as opposed to SFX)
  0001 word Channel1Pointer ; points to the data for channel 1
  0003 word Channel2Pointer ; points to the data for channel 2
  0005 word Channel3Pointer ; points to the data for channel 3
  0007 word Channel4Pointer ; points to the data for channel 4
  0009 word MetaPointer ; points to the list of vibrato specifications
Each channel is a stream of commands. The following are the most important commands:
  $00 $xx
    Sets playback speed to xx (each note plays for xx frames)
  $02 $xx
    Sets the duty-cycle values to xx. Accepted values: $00,$80,$40,$C0.
    This controls the type of sound produced by the NES hardware.
  $03 $xy
    Sets the volume and envelope of the sound. I think y is the volume and x is the envelope decay rate, or vice versa.
  $04 $nn $addr
    Declares a loop-end. The commands in between of the address $addr and this loop command are repeated nn times. If nn=0, loops infinitely.
    Loop nesting is not allowed, unless the outer nn=0 and inner nn>0.
  $05 $xx
    Sets the base note to xx. The value xx (in semitones) is added to each note played.
    Multiples of 12 give a difference of an octave.
  $06
    The following note will be played 50% longer (i.e. if the length is otherwise indicated 8 units, this makes it 12).
  $30
    The following note will be played 1/3 shorter (i.e. if the length is otherwise indicated 8 units, this makes it 8*2/3). This can be used to play triplets.
  $40..$5F
    Plays a note with length=2. The note played is (bytevalue-$40 + basenotevalue), except $40 means silence instead.
    The actual duration for the note is 2*playbackspeed frames,
    except if prefixed with the $06 byte or with the $30 byte.
  $60..$7F
    Plays a note with length=4. The note played is (bytevalue-$60 + basenotevalue), except $60 means silence instead.
  $80..$9F
    Plays a note with length=8. The note played is (bytevalue-$80 + basenotevalue), except $80 means silence instead.
  $A0..$BF
    Plays a note with length=16. The note played is (bytevalue-$A0 + basenotevalue), except $A0 means silence instead.
  $C0..$DF
    Plays a note with length=32. The note played is (bytevalue-$C0 + basenotevalue), except $C0 means silence instead.
  $E0..$FF
    Plays a note with length=64. The note played is (bytevalue-$E0 + basenotevalue), except $E0 means silence instead.
If you don't use vibrato, the vibrato specification pointer can be specified as 0. To use the sound engine, the following interface is available:
  jsr $8000  ; Call this once every NMI.
 
  lda #1
  jsr $8003 ; Call this when you want to start song number 1

  lda #$FC
  jsr $8003 ; Call this when you want to speed up music

  lda #$FD
  ldy 1  ; fade power
  jsr $8003 ; Call this when you want to initialize a volume fade

  lda #$FE
  jsr $8003 ; Call this when you want to stop all sfx and music

  lda #$FE
  jsr $8003 ; Call this when you want to stop all music