#include <stdio.h>
#include <iostream>
#include <fstream>
#include <string>
#include <stdio.h>
// Compass Soup programming language interpreter
// created by Brian MacIntosh (BMacZero)
// for http://codegolf.stackexchange.com/questions/61804/create-a-programming-language-that-only-appears-to-be-unusable
//
// 31 October 2015
struct Point
{
int x, y;
Point(int ix, int iy) { x = ix; y = iy; };
bool operator==(const Point &other) const
{
return other.x == x && other.y == y;
}
bool operator!=(const Point &other) const
{
return other.x != x || other.y != y;
}
};
struct Bounds
{
int xMin, xMax, yMin, yMax;
Bounds(int xmin, int ymin, int xmax, int ymax)
{
xMin = xmin; yMin = ymin; xMax = xmax; yMax = ymax;
}
bool contains(Point pt)
{
return pt.x >= xMin && pt.x <= xMax && pt.y >= yMin && pt.y <= yMax;
}
int getWidth() { return xMax - xMin + 1; }
int getHeight() { return yMax - yMin + 1; }
bool operator==(const Bounds &other) const
{
return other.xMin == xMin && other.xMax == xMax && other.yMin == yMin && other.yMax == yMax;
}
bool operator!=(const Bounds &other) const
{
return other.xMin != xMin || other.xMax != xMax || other.yMin != yMin || other.yMax != yMax;
}
};
int max(int a, int b) { return a > b ? a : b; }
int min(int a, int b) { return a < b ? a : b; }
Bounds hull(Point a, Bounds b)
{
return Bounds(min(a.x, b.xMin), min(a.y, b.yMin), max(a.x, b.xMax), max(a.y, b.yMax));
}
Bounds hull(Bounds a, Bounds b)
{
return Bounds(min(a.xMin, b.xMin), min(a.yMin, b.yMin), max(a.xMax, b.xMax), max(a.yMax, b.yMax));
}
Bounds programBounds(0,0,0,0);
char** programSpace;
Point execPtr(0,0);
Point execPtrDir(1,0);
Point dataPtr(0,0);
Point stdInPos(0,0);
bool breakpointHit = false;
char breakOn = 0;
/// reads the character from the specified position
char read(Point pt)
{
if (programBounds.contains(pt))
return programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin];
else
return 0;
}
/// read the character at the data pointer
char readData()
{
return read(dataPtr);
}
/// read the character at the execution pointer
char readProgram()
{
return read(execPtr);
}
/// gets the bounds of the actual content of the program space
Bounds getTightBounds(bool debug)
{
Bounds tight(0,0,0,0);
for (int x = programBounds.xMin; x <= programBounds.xMax; x++)
{
for (int y = programBounds.yMin; y <= programBounds.yMax; y++)
{
if (read(Point(x, y)) != 0)
{
tight = hull(Point(x, y), tight);
}
}
}
if (debug)
{
tight = hull(dataPtr, tight);
tight = hull(execPtr, tight);
}
return tight;
}
/// ensure that the program space encompasses the specified rectangle
void fitProgramSpace(Bounds bounds)
{
Bounds newBounds = hull(bounds, programBounds);
if (newBounds == programBounds) return;
// allocate new space
char** newSpace = new char*[newBounds.getWidth()];
// copy content
for (int x = 0; x < newBounds.getWidth(); x++)
{
newSpace[x] = new char[newBounds.getHeight()];
for (int y = 0; y < newBounds.getHeight(); y++)
{
Point newWorldPos(x + newBounds.xMin, y + newBounds.yMin);
newSpace[x][y] = read(newWorldPos);
}
}
// destroy old space
for (int x = 0; x < programBounds.getWidth(); x++)
{
delete[] programSpace[x];
}
delete[] programSpace;
programSpace = newSpace;
programBounds = newBounds;
}
/// outputs the current program space to a file
void outputToStream(std::ostream &stream, bool debug)
{
Bounds tight = getTightBounds(debug);
for (int y = tight.yMin; y <= tight.yMax; y++)
{
for (int x = tight.xMin; x <= tight.xMax; x++)
{
char at = read(Point(x, y));
if (debug && x == execPtr.x && y == execPtr.y)
stream << (char)178;
else if (debug && x == dataPtr.x && y == dataPtr.y)
stream << (char)177;
else if (at == 0)
stream << ' ';
else
stream << at;
}
stream << std::endl;
}
}
/// writes a character at the specified position
void write(Point pt, char ch)
{
fitProgramSpace(hull(pt, programBounds));
programSpace[pt.x - programBounds.xMin][pt.y - programBounds.yMin] = ch;
}
/// writes a character at the data pointer
void write(char ch)
{
write(dataPtr, ch);
}
/// writes a line of text horizontally, starting at the specified position
void writeLine(Point loc, std::string str, bool isSource)
{
fitProgramSpace(Bounds(loc.x, loc.y, loc.x + str.size(), loc.y));
for (unsigned int x = 0; x < str.size(); x++)
{
programSpace[x + loc.x][loc.y] = str[x];
// record locations of things
if (isSource)
{
switch (str[x])
{
case '>':
stdInPos = Point(loc.x + x, loc.y);
break;
case '!':
execPtr = Point(loc.x + x, loc.y);
break;
case '@':
dataPtr = Point(loc.x + x, loc.y);
break;
}
}
}
}
void advanceExecPtr()
{
execPtr.x += execPtrDir.x;
execPtr.y += execPtrDir.y;
}
void breakpoint()
{
breakpointHit = true;
outputToStream(std::cout, true);
std::cout << "[Return]: step | [Space+Return]: continue | [<char>+Return]: continue to <char>" << std::endl;
while (true)
{
std::string input;
std::getline(std::cin, input);
if (input.size() == 0)
{
break;
}
else if (input.size() == 1)
{
if (input[0] == ' ')
{
breakpointHit = false;
break;
}
else
{
breakOn = input[0];
breakpointHit = false;
break;
}
}
}
}
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("Usage: CompassSoup <source-file>");
return 1;
}
// open source file
std::ifstream sourceIn(argv[1]);
if (!sourceIn.is_open())
{
printf("Error reading source file.");
return 1;
}
programSpace = new char*[1];
programSpace[0] = new char[1];
programSpace[0][0] = 0;
// read starting configuration
std::string line;
int currentLine = 0;
while (std::getline(sourceIn, line))
{
writeLine(Point(0, currentLine), line, true);
currentLine++;
}
sourceIn.close();
// take stdin
std::string input;
std::cout << ">";
std::cin >> input;
std::cin.ignore();
writeLine(stdInPos, input, false);
// execute
while (programBounds.contains(execPtr))
{
if (execPtrexecPtrDir.x == 0 && execPtrexecPtrDir.y == 0)
{
printf("Implementation error: execPtr is stuck.");
break;
}
advanceExecPtr();
char command = readProgram();
// breakpoint control code
if (breakpointHit || (breakOn != 0 && command == breakOn))
{
breakOn = 0;
breakpoint();
}
switch (command)
{
case 'n':
execPtrDir = Point(0,-1);
break;
case 'e':
execPtrDir = Point(1,0);
break;
case 's':
execPtrDir = Point(0,1);
break;
case 'w':
execPtrDir = Point(-1,0);
break;
case 'x':
dataPtr.x--;
break;
case 'X':
dataPtr.x++;
break;
case 'y':
dataPtr.y--;
break;
case 'Y':
dataPtr.y++;
break;
case 'p':
advanceExecPtr();
write(readProgram());
break;
case 'j':
advanceExecPtr();
if (readData() == readProgram())
{
advanceExecPtr();
}
break;
case 'c':
write(0);
break;
case '*':
breakpoint();
break;
}
}
std::ofstream outputFile("result.txt");
outputToStream(outputFile, false);
outputToStream(std::cout, false);
outputFile.close();
}