Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
nfq wrote:
I watched your youtube video too, and you write really fast. Do you use a normal "qwerty" keyboard layout?
Thanks for watching. I do type fast, but the recording process was lagged somewhat, which artificially increased my apparent typing speed by a noticeable percentage. Which is good, because just like with TASes, it improved the entertainment (quality/quantity ratio). I use qwerty. Dromiceius> The article you linked to does not talk about QBasic's tokenization, though. That is closer to what C64 did. QBasic does not allow the meaning of program to change by tokenization/whitespace removal. (ANDY is a legal variable name in QB.) Arflech> That code was not really meant to be human-read, either. It is autogenerated by a program I wrote. (I.e. I first wrote the program in a clean manner, then compressed it by reducing all variable names and using the operators in a funny way, and then I fit it into C strings to be outputted by this intgen program, whose output you see here.) I posted it to provide contrast to the other post.
Post subject: How does one write obfuscated C code?
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Bisqwit wrote:
I first wrote the program in a clean manner, then compressed it by reducing all variable names and using the operators in a funny way
Let me post an example of such an iterative process. Here's a C program which produces season's greetings ― a different greeting depending on time of day.
#include <sys/time.h>
#include <stdio.h>
int main(void)
{
    time_t t;
    struct tm* tm;
    
    t = time(NULL); /* Get current time in seconds */
    tm = localtime(&t); /* Transform a timestamp to broken-down time, relative to user's timezone */
    
    switch(tm->tm_hour) /* Choose action from the hour value */
    {
        case 4:case 5:case 6:case 7:
        case 8:case 9:case 10:case 11:
            printf("Good morning!\n");
            break;
        case 12:case 13:case 14:
            printf("Good day!\n");
            break;
        case 15:case 16:case 17:
            printf("Good afternoon!\n");
            break;
        case 18:case 19:case 20:case 21:
            printf("Good evening!\n");
            break;
        default:
            printf("Good night!\n");
            break;
    }
    return 0;
}
This is revision 1. Anyone who knows the basic syntax of C can read exactly what happens here without strain. Now, to make it shorter by using data as a substitute for working logic:
#include <sys/time.h>
#include <stdio.h>
int main(void) /* revision 2 */
{
    static const char* const words[] = {"morning","day","afternoon","evening","night"};
    static const int per_hour[24] = {4,4,4,4, 0,0,0,0,0,0,0,0, 1,1,1, 2,2,2, 3,3,3,3, 4,4};

    time_t t;
    struct tm* tm;
    int greeting_index;
    
    t = time(NULL);
    tm = localtime(&t);
    
    greeting_index = per_hour[ tm->tm_hour ];
    printf("Good %s!\n", words[ greeting_index ] );
    return 0;
}
Furthermore, reduce the syntax:
#include <sys/time.h>
#include <stdio.h>
int main(void) /* revision 3 */
{
    static const char* const words[] = {"morning","day","afternoon","evening","night"};
    static const int per_hour[24] = {4,4,4,4, 0,0,0,0,0,0,0,0, 1,1,1, 2,2,2, 3,3,3,3, 4,4};

    time_t t = time(NULL);
    struct tm* tm = localtime(&t);
    
    return printf("Good %s!\n", words[ per_hour[ tm->tm_hour ] ] );
}
Express the data in a manner that reduces less source code space:
#include <sys/time.h>
#include <stdio.h>
int main(void) /* revision 4 */
{
    static const char* const words[] = {"morning","day","afternoon","evening","night"};
    static const char per_hour[24+1] = "444400000000111222333344";
    time_t t = time(NULL);
    struct tm* tm = localtime(&t);
    return printf("Good %s!\n", words[ per_hour[ tm->tm_hour ] - '0' ] );
}
Reduce number of redundant variables, simplify expressions, and shorten the variable names:
#include <sys/time.h>
/* #include <stdio.h> - not needed, guessed prototype for printf() is good enough */
int main(void) /* revision 5 */
{
    static const char* const w[] = {"morning","day","afternoon","evening","night"};
    time_t t = time(0);
    return printf("Good %s!\n", w[ "444400000000111222333344"[ localtime(&t)->tm_hour ] & 7 ] );
}
Finally, remove redundant white-space:
#include <sys/time.h>
int main(void)/* revision 6 */{time_t t=time(0);
static const char*const w[]={"morning","day","afternoon","evening","night"};
return printf("Good %s!\n",w["444400000001112223333444"[(localtime(&t))->tm_hour]&7]);}
In hindsight, yet one more data reduction:
#include <sys/time.h>
int main()/* revision 7*/{time_t t=time(0);return printf("Good %s!\n",
"morning\0day\0afternoon\0evening\0night"-194+2*"ppppaaaaaaaaeeegggllllppp"[localtime(&t)->tm_hour]);}/* 194 comes from 'a'*2 */
Or:
#include <sys/time.h>
int main()/* revision 7b*/{time_t t=time(0);return printf("Good %s!\n",
"day\0night\0morning\0evening\0afternoon"-64+"DDDDJJJJJJJJ@@@ZZZRRRRDD"[localtime(&t)->tm_hour]);}
Or:
#include <sys/time.h>
char*d="''''--------###===5555'' Good %s!\n\0day\0night\0morning\0evening\0afternoon";
int main()/* revision 8*/{time_t t=time(0);return printf(d+25,d+d[localtime(&t)->tm_hour]);}
If you rely on the fact that time() returns an int (or a long), and that the value measures seconds since 1970-01-01 00:00 GMT, and you know your timezone, you can discard localtime() and simply calculate the hour from the time() return value by modulo mathematics, and you could discard the one #include. For the sake of portability, we'll skip that. Anyway, this is how such messy source code is produced. It has its uses in IOCCC, novelty and code generation, but not when it is meant to be maintained by humans.
Joined: 7/2/2007
Posts: 3960
One question: how is subtracting '0' equivalent to bitwise-anding 7? '0' is 48, or 110000 in binary; 7 is 111 in binary. Let's try 57, or 111001. Subtracting off '0' gives 1001 (9); bitwise-anding 7 gives 001 (1).
Pyrel - an open-source rewrite of the Angband roguelike game in Python.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Derakon wrote:
One question: how is subtracting '0' equivalent to bitwise-anding 7? '0' is 48, or 110000 in binary; 7 is 111 in binary. Let's try 57, or 111001. Subtracting off '0' gives 1001 (9); bitwise-anding 7 gives 001 (1).
Characters 0123456789 are in the ASCII range 0x30 .. 0x39. In this program, only characters 01234 (0x30..0x34) were used. '4' - '0' gives the value 4. 0x34 & 0x07 gives the value 4. '0' - '0' gives the value 0. 0x30 & 0x07 gives the value 0. 0x39 (57, '9') is outside the set of '0', '1', '2', '3' and '4' for which the expression was written to work and give proper values. It would also work for '5', '6' and '7', but not for '8' or '9'.
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Fun with templates. (Not for the faint of heart.)
#include <set>
#include <string>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <functional>
#include <cctype>

std::string processString(std::string s)
{
    s.erase(std::remove_if(s.begin(), s.end(), std::not1(std::ptr_fun(isalpha))), s.end());
    std::transform(s.begin(), s.end(), s.begin(), toupper);
    return s;
}

int main()
{
    typedef std::istream_iterator<std::string> InIt;
    std::set<std::string> words;
    std::transform(InIt(std::cin), InIt(), std::inserter(words, words.end()), processString);
    std::copy(words.begin(), words.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
}
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
Fun with templates. (Not for the faint of heart.)
Primordially, one is inclined to ask… is that C++? Then again, I've got very similar blank stares regarding this PHP code:
    $keys = array_filter(someParser::GetAllSomeKeys(),
                create_function('$key', "return \$key['type'] == 'some_info';"));
Even though that is fundamentally much more basic. For the record, this code is functionally equivalent to this:
    $keys = Array();
    foreach(someParser::GetAllSomeKeys() as $index => $value)
        if($value['type'] == 'some_info')
            $keys[$index] = $value;
Post subject: Re: adventure game
Joined: 8/22/2009
Posts: 58
Bisqwit wrote:
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> list; redo:
    for(size_t a=0; a<NItems>= 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>
    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> 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> move(Eq& target, const std::string& what, bool all) {
        std::deque<std> 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> 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>& 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>= 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> > rooms;
    Room& GenerateRoom(long x,long y,const Room& model, int seed) {
        std::srand((x<<12)^y);
        std::pair<std::map<long>::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> >::const_iterator
            i = rooms.find(x);
        if(i == rooms.end()) return ' ';
        std::map<long>::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>
{
    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> 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>=150 && life-l<150>=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.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> 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> 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> 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> 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
Is there a walkthrough for this? I would like to input a series of commands to beat the game, I think it would be fun.
Post subject: Re: adventure game
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
fullquote != win Re: walkthrough, make one! Note that the dungeon is different on different operating systems / c library versions due to its reliance on srand/rand.
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Speaking of getting ye flask, I really miss those text adventures. But it's true that in the 80's many of them were quite picky about what they supported and how flexible they were (often perhaps because of the limited amount of RAM in the systems in question). More modern interpreters can understand surprisingly complex sentences. For example you might have a session along the lines of: > open the door. You can't, it's locked. > open the red box and examine it You open the red box. The red box is empty. > open the blue box and examine it You open the blue box. There's a golden key and a silver key in the blue box. > take the golden key from the box and open the door with it You take the golden key from the blue box. You open the door with the golden key. Especially the last command requires quite a bit of deduction from the part of the interpreter. While simple in principle, it still manages to give a great feeling of flexibility because the interpreter does not require you to spell out every minute detail, and instead can deduce what you mean. For example, you don't have to specify the color of the box where you want the key taken from, as the interpreter can deduce that since there are two boxes and a golden key only in one of them, you meant to take it from that one. Likewise there are three things to which the last word "it" could refer to: The golden key, the box and the door. However, "open ... with it" makes only sense with the key (which is now in your inventory), so it deduces that "it" refers (unambiguously) to it.
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
Warp wrote:
> take the golden key from the box and open the door with it
There is no golden key in the red box. You take the golden key from the blue box. You cannot open the door with the blue box. You cannot open the door with the door. You open the door with the golden key. Simple: Iterate through ambiguous choices and choose the first-match, and yank error messages. Well, simple in theory. Practice, not so much. Though these images illustrate other reasons. EDIT: By the way, I would like "open the door with the golden key in the box" better.
Banned User, Former player
Joined: 3/10/2004
Posts: 7698
Location: Finland
Bisqwit wrote:
Simple: Iterate through ambiguous choices and choose the first-match, and yank error messages.
Of course it has to detect ambiguous cases. For example "take the key from the box" has two possible interpretations because there are two keys in the blue box. An interesting question is that if you had both keys in your inventory and you typed "open the door with the key", should it announce the command to be ambiguous, or should it automatically choose the proper key?
HHS
Active player (282)
Joined: 10/8/2006
Posts: 356
I'm not sure if "Open the door with the door" is even a possible interpretation of "Open the door with it". The word "it" usually doesn't refer to something introduced in the sentence itself. I remember making a graphical text adventure in Hypercard on an old Macintosh when I was 9. I used buttons to cover up objects which had been picked up, lol. It even had a fighting system in it. I think I ran into the 32K code size limit. Don't know where I left the disk, though. The computer had some other text adventures such as Castle of Ert and Tim's World, which were made in a program called World Builder. Recently I found another WB game called A mess of trouble, which I was able to run in VMAC, also a neat game with good looking monochrome graphics. I'm thinking of designing a natural language processing system, but currently I have no plans for parsing entered text, only for generating text in multiple languages based on stored sentence information. My goal is to implement this on devices with limited resources using very little RAM. My previous attempt at this was able to produce many sentences in Norwegian, Swedish and English but had some design flaws in it, so I decided to start over. Here is a page with some screenshots of it in action (I know that some of the words are wrong since I'm very bad at Swedish): http://gigasoft.byethost15.com/nlp.htm
Editor, Active player (296)
Joined: 3/8/2004
Posts: 7469
Location: Arzareth
HHS wrote:
I'm not sure if "Open the door with the door" is even a possible interpretation of "Open the door with it". The word "it" usually doesn't refer to something introduced in the sentence itself.
At least it is a valid interpretation for "open the door with itself". I'm not sure whether "itself" is one of the possible interpretation of "it". The parse trees shown earlier were produced with Link [link] btw.
Active player (353)
Joined: 1/16/2008
Posts: 358
Location: The Netherlands
I kind of liked this one when someone presented it to me
In mobile phone transport, these devices are mass-packed into a large without their retail packing. In our case, we have only (equal) phones of size DxWxH cm (given D<=W<=H). In the larger box, the phones are all aligned equally. Inbetween two phones and between a phone and the 'wall' needs to be 2cm of polystyrene foam in all directions. Find an algorithm that efficiently determines the 'best' size for the surrounding box, given an amount N>0 of phones. The 'best' size is the one that has the least external volume. (unfilled space in the box is disregarded) The solution should also work for unrealistically large N (assume all phones to be box-shaped, so no space can be gained by interleaving them)
TASes: [URL=http://tasvideos.org/Movies-298up-Obs.html]Mr. Nutz (SNES), Young Merlin 100% (SNES), Animaniacs 100% (SNES)[/URL]
Player (120)
Joined: 2/11/2007
Posts: 1522
HHS wrote:
I remember making a graphical text adventure in Hypercard
Hypercard was definitely where I learned that programming was fun!
I make a comic with no image files and you should read it. While there is a lower class, I am in it, and while there is a criminal element I am of it, and while there is a soul in prison, I am not free. -Eugene Debs
arflech
He/Him
Joined: 5/3/2008
Posts: 1120
DaTeL237 wrote:
I kind of liked this one when someone presented it to me
In mobile phone transport, these devices are mass-packed into a large without their retail packing. In our case, we have only (equal) phones of size DxWxH cm (given D<=W<H>0 of phones. The 'best' size is the one that has the least external volume. (unfilled space in the box is disregarded) The solution should also work for unrealistically large N (assume all phones to be box-shaped, so no space can be gained by interleaving them)
I think it has something to do with the cube root of N, like stacking them n times in the D direction, where n is the smallest factor of N that is not smaller than the cube root of N, and stacking them m times in the W direction, where m is the smallest factor of N/n that is not smaller than the square root of N/n, and finally stacking them N/(mn) times in the H direction. This algorithm would, for example, have 110 phones packed 5 times in the D direction, 11 times in the W direction, and 2 times in the H direction. hmmm...maybe in that first step it would be better to use "square root" instead of "cube root" and in that case, 110 phones would be packed 11 times in the D direction, 5 times in the W direction, and 2 times in the H direction, which intuitively looks like the optimal packing but then this fails for 64 phones, because it would say 8*4*2, although 4*4*4 would be better More generally, you need to find three integers n, m, and r with the properties that N=nmr, r<=m<=n, and the three integers are in some sense as close as possible; then the optimal packing is n times in the D direction (to use the larger H*W*2cm pieces of foam as efficiently as possible), m times in the W direction, and r times in the H direction. ...but maybe that only works if H and D are very close, because evidently it is more efficient to pack 4 iPhones in a single stack of 4 than 2*2*1
i imgur com/QiCaaH8 png
Active player (353)
Joined: 1/16/2008
Posts: 358
Location: The Netherlands
arflech wrote:
More generally, you need to find three integers n, m, and r with the properties that N=nmr, r<=m<=n, and the three integers are in some sense as close as possible;
well, you need to find N<n*m*r... because empty space is allowed, it just also counts as outer size (did that make sense? :P e.g. for N=7 you'd have n=m=r=2)
TASes: [URL=http://tasvideos.org/Movies-298up-Obs.html]Mr. Nutz (SNES), Young Merlin 100% (SNES), Animaniacs 100% (SNES)[/URL]
arflech
He/Him
Joined: 5/3/2008
Posts: 1120
so...is it just n=m=r=ceil(N^(1/3))?
i imgur com/QiCaaH8 png
Active player (353)
Joined: 1/16/2008
Posts: 358
Location: The Netherlands
arflech wrote:
so...is it just n=m=r=ceil(N^(1/3))?
No.... as in in that case you'd be packing N=3 into a a box where 2x2x2=8 fit I'm pretty sure there's no single formula you can fill it to get the answer.... which is why this is in the programming topic and not the math topic
TASes: [URL=http://tasvideos.org/Movies-298up-Obs.html]Mr. Nutz (SNES), Young Merlin 100% (SNES), Animaniacs 100% (SNES)[/URL]
Hoe
Joined: 7/31/2004
Posts: 183
Location: USA
Here's an abstract class I made for a recent PHP project, http://red-stars.net/random/Options.txt Example:
class MyClass extends Options {
	public $Property;

	const MyOption = "MyOption";
	const AnotherOption = "AnotherOption";

	protected static $_defaultOptions = array(self::AnotherOption = "default value!");

	public function __construct($property, $options = array()) {
		$this->Property = $property;
		$this->SetOptionalProperties($options); // this allows Options to access the optional values.
	}
}

$instance = new MyClass("property's value", array(MyClass::MyOption = "my option value!"));
echo "MyOption: '{$instance->MyOption}', AnotherOption: '{$instance->AnotherOption}', Property: '{$instance->Property}'";
Output:
MyOption: 'my option value!', AnotherOption: 'default value!', Property: 'property's value'
... you get it?! It's pretty neat :) (requires php5.3 so it can use 'late static binding')
Tub
Joined: 6/25/2005
Posts: 1377
I think that can be done much easier without relying on those static const members. consts are a pain in php, as they cannot be overloaded. As you noticed, your implementation relies on internal class specifics that may change between versions. Here's my design:
class MyClass extends Options {
  public $options = array(
    'optionname' => 'default value',
    'secondoption' => null, // no default
    'thirdoption' => 'third default',
  );

  public function __construct($initialvalues = null) {
    parent::__construct($initialvalues);
  }
}
I didn't bother writing down the respective implementation of Options, but it should be shorter than yours. Any use cases my design doesn't cover?
m00
Hoe
Joined: 7/31/2004
Posts: 183
Location: USA
Tub wrote:
I think that can be done much easier without relying on those static const members. consts are a pain in php, as they cannot be overloaded. As you noticed, your implementation relies on internal class specifics that may change between versions.
You are right, and that would be a better implementation, though there's a reason I used constants: Non-reliance upon strings. I wanted the 'optional parameters' to be as explicitly addressed as possible. Here's some sample code calling a class that uses it as-is.
FrameController::AddControl(
	new FrameControl(
		function($control, $page) {
			$user = User::GetUserFromGlobal();
			if (!empty($user))
				$page->Assign("User", $user);
		}, array(
			FrameControl::RequiredCookies => array("UserName", "Password"),
			FrameControl::Priority => FrameControlPriorities::Highest,
			FrameControl::KeepRunning => true
		)
	)
);
As you can see, I like things explicit, very very explicit :) (did C# for years) There's also a useful quirk to the constants, you can have 'aliases.' Like: const Property = "blah"; const Alias = "blah"; will address the same value. This is going to be nice when I try to make my project cross-compatible with doctrine's YAML
Tub
Joined: 6/25/2005
Posts: 1377
Hoe wrote:
I wanted the 'optional parameters' to be as explicitly addressed as possible.
because writing FrameControl::Priority is more explicit than 'Priority'? I'd argue that the explicit version is even worse, as the scope is apparent by context and you're just adding noise. Unfortunately the consts don't give you compile-time-checking either (meaning: typing errors aren't caught by php -l), so the only reason to prefer the consts would be using a smart editor with auto-completion and const-highlighting. (btw: if you know such an editor that works on linux and doesn't take 3 minutes to start like eclipse, please tell me ;))
Hoe wrote:
There's also a useful quirk to the constants, you can have 'aliases.' Like: const Property = "blah"; const Alias = "blah";
add
class MyClass extends Options {
  ...
  public $aliases = array(
    'Alias' => 'Property',
    'Alias2' => 'AnotherProperty',
  );
}
and you can hide the corresponding logic for alias handling inside the Options class. Not a reason to use consts.
m00
Hoe
Joined: 7/31/2004
Posts: 183
Location: USA
Tub wrote:
Hoe wrote:
I wanted the 'optional parameters' to be as explicitly addressed as possible.
because writing FrameControl::Priority is more explicit than 'Priority'? I'd argue that the explicit version is even worse, as the scope is apparent by context and you're just adding noise. Unfortunately the consts don't give you compile-time-checking either (meaning: typing errors aren't caught by php -l), so the only reason to prefer the consts would be using a smart editor with auto-completion and const-highlighting. (btw: if you know such an editor that works on linux and doesn't take 3 minutes to start like eclipse, please tell me ;))
I've yet to find an editor that has offered more than notepad++ and remained usable, I couldn't muster using Eclipse due to the speed. I don't like how 'locked in' most PHP IDE's feel. PHP is giving me great difficulty due to poor debuggers/profilers. I can only get xdebug to semi-work, I end up doing a lot of print_r. You're absolutely right about the array in general being a better/more direct method, though there is another thing I like about the constant's approach, they're much easier to document using Doxygen, each one gets its own section and they can further be grouped.
Joined: 7/2/2007
Posts: 3960
Hoe wrote:
I've yet to find an editor that has offered more than notepad++ and remained usable
vim! Or emacs, if you must. Either of them will, once you've passed the initial learning curve, greatly improve the rate at which you can edit source code. They don't provide the benefits of an IDE, but as a general rule I've not found myself hurting for that lack. Granted I don't work on super-mega-ultra projects with hundreds of thousands of lines of code; in such a situation an IDE would be more critical.
Pyrel - an open-source rewrite of the Angband roguelike game in Python.