Adam's Lair Forum

game development and casual madness
It is currently 2017/04/26, 00:18

All times are UTC + 1 hour [ DST ]




Post new topic Reply to topic  [ 3 posts ] 
Author Message
 Post subject: Your Craziest Project
PostPosted: 2016/02/18, 22:21 
Member
Member

Joined: 2015/11/04, 20:02
Posts: 69
Location: Canada
Role: Hobbyist
It's in the title!

What is the most absurd project you've ever tackled?
Why did you do it?
Were you successful?
Still working on it?
What can you tell us about it?

It doesn't have to be your largest project, or necessarily your most ambitious.
Instead, I'm looking that one project in your portfolio that people might look at and question your sanity.

Let's see it! :D


Last edited by ARC on 2016/02/18, 22:37, edited 1 time in total.

Top
 Profile  
 
PostPosted: 2016/02/18, 22:37 
Member
Member

Joined: 2015/11/04, 20:02
Posts: 69
Location: Canada
Role: Hobbyist
What was your most absurd project?
A stack based virtual machine and scripting language to go with it, written in C++

Why?
Well, I'm not entirely sure. I guess I wanted to learn about the low level details of how programming languages work. What better way to do that than to write your own, compiler and all?

Was it successful?
Eh. I'll give it a 6/10 on the successful-o-meter. I got it working to the point where I could hand write small byte-code programs and execute them. It was pretty cool actually. The real trouble I had with it was writing the compiler itself. Compilers are hard. Really. Freaking. Hard.

Still working on it?
Not really! I might revisit it sooner or later, probably with a rewrite.
It's an incredibly alluring thing, authoring your own language.

What can you tell us about it?
Not a whole lot, though I will leave this code snippet of the core byte-code evaluation logic (It was an interpreted language run on C++, heck if I'm writing a native compiler). I think it's an interesting piece of code to peruse. I challenge anyone to decipher some of it (further than the comments themselves), especially if you're not a C++ programmer :D
This is the "heart" of the language, if you will:
Code:
void Violet::eval()
   {
      dword instruction = program[ip].i;
      switch (instruction)
      {
         // pop
         // Pops and discards the top of the stack
         case pop:
         {
            if (sp > -1){ sp--; }
            return;
         }
         // irload [addr]
         // Load the value off the heap located at address [addr]
         case irload:
         {
            memcpy(&stack[++sp], &program[++ip], _vIntSize);
            stack[sp].type = ValueType::INT;
            return;
         }
         // irstore [addr]
         // Store the top of the stack in the heap at address [addr]
         case irstore:
         {
            memcpy(&memory[allocate(_vObjSize)], &stack[sp--], _vIntSize);
            return;
         }
         // narray [type] [count]
         // Create a new array of type [type] and length [count] and push its pointer to the stack
         case narray:
         {
            x.type = (byte)program[++ip].r;
            // Allocate memory for the array and push its pointer
            stack[++sp].r = allocate(_vArrayHeaderSize + (program[++ip].i * _vIntSize));

            // Write the array header
            vArrayHeader header{ x.type, program[ip].i };
            memcpy(&memory[stack[sp].r], &header, _vArrayHeaderSize);

            // Set type of stack head to reference
            stack[sp].type = ValueType::REF;
            return;
         }
         // arrput [ref] [index] [value]
         // Inserts [value] into the array pointed to by [ref] at position [index]
         case arrput:
         {
            // pop [ref]
            a = stack[sp--];
            // pop [index]
            b = stack[sp--];
            // pop [value]
            c = stack[sp--];
            // fetch the array header
            vArrayHeader header;
            memcpy(&header, &memory[a.r], _vArrayHeaderSize);

            if (b.i > header.size){ error(Error{ VM_ARRAY_OOBOUNDS }); return; } // check out of bounds
            if (c.type != header.type){ error(Error{ VM_TYPE_MISMATCH }); return; } // check type mismatch

            // checks passed, switch on type size and insert the value
            memcpy(
               &memory[a.r + _vArrayHeaderSize + b.i], // offset by array pointer, array header, and index
               &c, // address of value
               _vObjSize // size of value
            );
            return;
         }
         // ipush [arg]
         // Pushes integer [arg] onto the stack
         case ipush:
         {
            stack[++sp].i = program[++ip].i;
            stack[sp].type = ValueType::INT;
            return;
         }
         // iadd [arg1] [arg2]
         // Adds integers [arg2] plus [arg1] and pushes the result to the stack
         case iadd:
         {
            b = stack[sp--];
            a = stack[sp--];
            stack[++sp].i = a.i + b.i;
            return;
         }
         // isub [arg1] [arg2]
         // Subtracts integers [arg2] minus [arg1] and pushes the result to the stack
         case isub:
         {
            b = stack[sp--];
            a = stack[sp--];
            stack[++sp].i = a.i - b.i;
            return;
         }
         // imul [arg1] [arg2]
         // Multiplies integers [arg2] by [arg1] and pushes the result to the stack
         case imul:
         {
            b = stack[sp--];
            a = stack[sp--];
            stack[++sp].i = a.i * b.i;
            return;
         }
         // idiv [arg1] [arg2]
         // Divides integers [arg2] by [arg1] and pushes the result to the stack
         case idiv:
         {
            b = stack[sp--];
            a = stack[sp--];
            stack[++sp].i = a.i / b.i;
            return;
         }
         // ilthan [arg1] [arg2]
         // Tests if integers [arg1] < [arg2] and pushes result to the stack
         case ilthan:
         {
            b = stack[sp];
            a = stack[sp - 1];
            stack[++sp].i = ((a.i < b.i) ? 1 : 0);
            return;
         }
         // iequal [arg1] [arg2]
         // Tests if integers [arg1] is equal to [arg2] and pushes result to the stack
         case iequal:
         {
            b = stack[sp--];
            a = stack[sp--];
            stack[++sp].i = ((a.i == b.i) ? 1 : 0);
            return;
         }
         // jmp [addr]
         // Unconditional jump to [addr]
         case jmp:
         {
            ip = program[++ip].i;
            return;
         }
         // jmpt [addr]
         // Conditional jump to [addr] if stack is truthy
         case jmpt:
         {
            if (stack[sp].i <= 0){
               ip++; ip++;
               return;
            }
            else{
               ip = program[++ip].i;
               sp--;
               return;
            }
         }
         // jmpf [addr]
         // Conditional jump to [addr] if stack is falsey
         case jmpf:
         {
            if (stack[sp].i > 0){
               ip++; ip++;
               return;
            }
            else{
               ip = program[++ip].i;
               sp--;
               return;
            }
         }
         // gload [addr]
         // Loads a global from program memory
         case gload:
         {
            checkOverflow();
            stack[++sp] = stack[fp + program[++ip].i];
            return;
         }
         // store [addr]
         // Stores the top of the stack in [addr]
         case gstore:
         {
            x = stack[sp--];
            memory[program[++ip].i];
            return;
         }
         // call [addr] [argc]
         // Performs a function call. Pops [argc] number of arguments and passes them to a new call frame
         case call:
         {
            /*
               1. Push parameters on in reverse order
               2. Push return address of call site (ip)
               3. Push local variables in order
               Call Stack:
               [loc 1] 10
               [loc 2] 9
               [loc..] 8
               [fp   ] 7
               [raddr] 6
               [arg 1] 5
               [arg 2] 4
               [arg..] 3
               [old..] ..
               [old..] ..
            */

            b.r = program[++ip].r; //call address

            //push arguments
            a.i = program[++ip].i; //num args
            /*for (int i = 0; i < a; i++){
            stack[++sp] = program[++ip];
            }*/

            //push state
            stack[++sp] = a;   //num args
            stack[++sp].i = fp;   //frame pointer
            stack[++sp].i = ip;   //return address
            //stack[++sp] = sp;   //stack pointer
            fp = sp;         //set frame pointer

            ip = b.i;   //set instruction pointer to call address
            return;
         }
         // ret
         // Returns the top of the stack and collapses the current stack frame
         case ret:
         {
            //un-roll the function call
            x = stack[sp--];   //store return argument
            sp = fp;         //jump over local variables
            ip = stack[sp--].i;   //set instruction pointer to return addr
            fp = stack[sp--].i;   //roll back frame pointer
            a = stack[sp--];   //get num args to discard
            sp -= a.i;         //jump over arguments
            stack[++sp] = x;   //push return value
            return;
         }
         // print [addr]
         // Placeholder. Prints value to std::cout
         case print:
         {
            f_print();
            break;
         }
         // halt
         // Halts the virtual machine
         case halt:
         {
            std::cout << "Halt\n";
            bhalt = true;
         }
         // This happens when we can't properly interpret the next instruction
         // Throw an error and halt execution
         default:
         {
            error(Error{VM_INSTRUCTION_FAULT});
            cfaults++;
            break;
         }
         return;
      }
   }

(A lot of that is very much not standard C++, by the way. So don't get too scared of the language if you've never used it before. I'm doing some utterly disgusting C-style stuff there in the name of performance and ultra low level functionality.)


Top
 Profile  
 
PostPosted: 2016/02/19, 22:10 
Site Admin
Site Admin
User avatar

Joined: 2013/05/11, 22:30
Posts: 1948
Location: Germany
Role: Professional
This is an awesome idea for a thread.

ARC wrote:
What was your most absurd project?
A stack based virtual machine and scripting language to go with it, written in C++

Really like your absurd project though, and I can get behind having fun writing your own virtual machine. It's kind of beautiful to write something that is simple, elegant and consists only of a handful of straightforward operations - but at the same time earning the emergent complexity of all the programs one could write using only this minimal instruction set.

That said...
ARC wrote:
The real trouble I had with it was writing the compiler itself. Compilers are hard. Really. Freaking. Hard.
Yep. :D

I'm not sure what I would describe as my own craziest project, so I'll just list some of them that come to mind*. They were mostly short-lived side projects I did back in school and had some fun with:

* cringe-worthy early game things not included

Chatbot Stuff

I attempted to write multiple chatbots, ranging from random output over pattern matching to replicating previously heard phrases and modifying them slightly. The results were never that great, but it was fun enough and at one point, I sent some of the prototypes into random IRC chatrooms and observed how users would react. Ironically, the one with the highest rate of success (i.e. being perceived as a human for the longest time, like, about a minute) was one that just put out random pre-defined trash phrases and put in some names.

Ants

A visualization of a certain aspect of swarm / group behavior of ant colonies, because I wanted to show my biology course an example of how a simple set of rules (4-5) would lead to complex behavior (forming a continuous path to food and optimizing it for shortest length). It worked, but also consumed easily 95% of the time I needed to prepare a talk on swarm / group behavior in general, so that kind of backfired on the time management part.

Evolution Stuff

Various attempts at coding "life simulations", mostly starring bacteria. Each had a genetic code with random mutations over time, and the most successful genome was tracked. The first version's genome simply had some predefined properties that would change, while the second version was more like (I'll just borrow some terminology here) a "byte code" that would be executed in a "virtual machine". Veeery limited and not by far as cool as it may sound. It was fun for a while and I spent hours just watching that tiny "evolution" that took place, but abandoned the project when I concluded that it simply didn't have the capabilities to produce something I couldn't anticipate or "cheat into existence" by providing an implementation shortcut for some behaviors.

Random.. uhm.. stuff

A program that just put out entirely random 32x32 bitmaps, because young me got fascinated by the fact that, theoretically, you could see anything on the output, like, whoa. As you can imagine, the outcome was just endless streams of noise and I discarded the program soon after.

So.. summarizing this:
Why? Curiosity, I think?
Was it successful? Nope. It crashed and burned, but it was a lot of fun in the process.
Still working on it? Nah, it's over.

Allright, let's hear some more. Tell me about your crazy project graveyards :D

_________________
Blog | GitHub | Twitter (@Adams_Lair)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC + 1 hour [ DST ]


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group