/* The original version of this program can be found at http://damb.dk */
#include <windows.h>
#include <stdio.h>
#include <limits.h>
#include <list>
#include <time.h>
#include <string>
#include <fstream>
#include <map>
#include <iostream>

#ifdef __BORLANDC__
  #pragma warn -8030
  #pragma warn -8027
#endif

#define MAX_DEPTH 256
#define X_SIZE 33
#define Y_SIZE 24
#define FREQ   50 // 50 tick/sec

HANDLE StdOut, StdIn;
int YOffset;
void GotoXY(int x, int y);
void GetXY(int &x, int &y);
BOOL IsKeyPressed(int Key);
void OutCharXY(int x, int y, char ch, WORD attr);
BOOL KeyHit();
WORD GetChar();
void ShowCaret(BOOL on);
int GetYOffset();
int GetYSize();
int GetXSize();
void Intro();
void ClearScreen();
int Score;

unsigned char Maze[Y_SIZE][X_SIZE] =
{
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,0,1},
  {1,0,1,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,0,1,0,1,0,1,0,1,0,0,0,0,0,0,1},
  {1,0,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,1,1,0,1},
  {1,0,1,0,1,1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,0,0,0,0,1},
  {1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,0,1,1,1,1,0,1,0,1,0,1,0,1,0,1,1,0,1},
  {1,0,1,0,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0,1,0,1,1,0,1},
  {1,0,1,0,1,1,1,0,1,1,1,1,1,0,1,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,0,1},
  {1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,1},
  {1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1},
  {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1},
  {1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
  {1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,1},
  {1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1},
  {1,0,1,0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,1},
  {1,0,1,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,1,0,1},
  {1,0,1,0,1,0,0,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,0,0,0,1,0,1},
  {1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1},
  {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1,0,1,0,1},
  {1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,1,1,1,1,0,1,0,1,0,1},
  {1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1},
  {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};

class HighScoreClass
{
public:
  HighScoreClass(const char *filename) : FileName(filename)
  {
    std::ifstream f(FileName.c_str());
    if(!f)
      return;
    while(Table.size() < 10)
    {
      std::string Line;
      std::getline(f, Line);
      std::string::size_type idx = Line.find(' ');
      if(idx == Line.npos)
        return;
      std::string ScoreStr = Line.substr(0, idx);
      std::string Name = Line.substr(idx + 1);
      int Score = strtol(ScoreStr.c_str(), 0, 10);
      Table.insert(std::make_pair(Score, Name));
    }
  }
  void CheckInsert(int Score)
  {
    bool Insert = false;
    if(Table.size() < 10)
      Insert = true;
    else
    {
      std::multimap<int, std::string>::iterator i = Table.begin();
      if(i->first < Score)
        Insert = true;
     }
     if(Insert)
     {
       if(Table.size() >= 10)
         Table.erase(Table.begin());
       printf("Congratulations, you entered the highscorenEnter your name:");
       fflush(stdout);
       std::string Name;
       std::getline(std::cin, Name);
       Table.insert(std::make_pair(Score, Name));
     }
  }
  void PrintHighScore()
  {
    std::multimap<int, std::string>::reverse_iterator i;
    printf("Highscore Table:n");
    for(i = Table.rbegin(); i != Table.rend(); i++)
      printf("%5d %sn", i->first, i->second.c_str());
  }
  ~HighScoreClass()
  {
    std::ofstream f(FileName.c_str());
    if(!f)
    {
      printf("Failed to open highscore file!n");
      return;
    }
    std::multimap<int, std::string>::iterator i;
    for(i = Table.begin(); i != Table.end(); i++)
      f << i->first << " " << i->second.c_str() << std::endl;
  }

private:
  std::string FileName;
  std::multimap<int, std::string> Table;
  HighScoreClass(); // Don't let the user create me without a filename, use the other constructor
};

void ShowStatus(int Energy, int NumBombs)
{
  char Buffer[256], *s;
  int i;

  sprintf(Buffer, "%5d ", Energy);
  for(s = Buffer, i = 0; *s; s++, i++)
    OutCharXY(i, -1, *s, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
  for(; NumBombs && i < X_SIZE - 6; i++, NumBombs--)
    OutCharXY(i, -1, (char )224, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
  for(; i < X_SIZE - 6; i++)
    OutCharXY(i, -1, ' ', FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);

  sprintf(Buffer, "%5d", Score);
  for(s = Buffer; *s; s++, i++)
    OutCharXY(i, -1, *s, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
}

int Random(int Range)
{
  return (rand() >> 3)%Range;
}

class Point
{
public:
  Point(int x_, int y_) : x(x_), y(y_)
  {}
  Point() : x(0), y(0) {}
  int x;
  int y;
};

enum ObjTypeEnum
{
  Path, // Must be 0 due to defintion of Maze[][]
  Free = Path,
  Wall, // Must be 1 due to defintion of Maze[][]
  Man,
  Sharp,
  BombTreasure,
  Bomb,
  Dot
};

class ObjectBaseClass
{
public:
  ObjectBaseClass(int x, int y, int freq, ObjTypeEnum Type) : ObjType(Type)
  {
     XPos = x;
     YPos = y;
     Period = FREQ/freq;
     Tick = 0;
  }
  virtual ~ObjectBaseClass() {} ;
  virtual bool Trigger() = 0;
  virtual bool IsAt(int x, int y)
  {
    return XPos == x && YPos == y;
  }
  virtual bool IsA(ObjTypeEnum type)
  {
    return ObjType == type;
  }
  int GetXPos() {return XPos;}
  int GetYPos() {return YPos;}
protected:
  friend class ObjectListClass;
  int Period;
  int Tick;
  int XPos, YPos;
  const ObjTypeEnum ObjType;
};

class ObjectListClass : private std::list<ObjectBaseClass *>
{
public:
   void Add(ObjectBaseClass *Obj)
   {
     push_back(Obj);
     if(Obj->IsA(Man))
       ManObj = (class ManObjectClass *)Obj;
     else if(Obj->IsA(Sharp))
       NumSharp++;
   }
   bool RemoveObject(int x, int y, ObjTypeEnum Type)
   {
     for(iterator i = begin(); i != end(); i++)
     {
        if((*i)->IsAt(x, y) && (*i)->IsA(Type))
        {
          if((*i)->IsA(Man))
            ManObj = 0;
          else if((*i)->IsA(Sharp))
            NumSharp--;
          delete *i;
          erase(i);
          return true;
        }
     }
     return false;
   }
   class ManObjectClass *FindMan()
   {
     return ManObj;
   }
   bool Trigger()
   {
     iterator idx = begin();
     while(idx != end())
     {
        if((*idx)->Tick++ >= (*idx)->Period)
        {
           (*idx)->Tick = 0;
           if(!((*idx)->Trigger()))
           {
              if((*idx)->IsA(Sharp))
                NumSharp--;
              else if((*idx)->IsA(Man))
                ManObj = 0;
              delete *idx;
              idx = erase(idx);
           }
           else
             idx++;
        }
        else
          idx++;
     }
     return ManObj ? true : false;
   }
   int GetNumSharp()
   {
     return NumSharp;
   }
private:
   class ManObjectClass *ManObj;
   int NumSharp;
};

ObjectListClass ObjectList;

class TimedObjectClass : public ObjectBaseClass
{
public:
  TimedObjectClass(int x, int y, ObjTypeEnum objType, int timeout, char ch, WORD attr) :
     ObjectBaseClass(x, y, 10, objType)
  {
     Timeout = timeout;
     Ch = ch;
     Attrib = attr;
     Counter = 0;
     DrawMe(true);
  }
  virtual bool Trigger()
  {
    return Counter++ < Timeout ? true : false;
  }
  virtual ~TimedObjectClass()
  {
    DrawMe(false);
  }
protected:
  virtual void DrawMe(bool on)
  {
    OutCharXY(XPos, YPos, on ? Ch : ' ', Attrib);
  }
  WORD Attrib;
  int  Timeout;
  int Counter;
  char Ch;
};

class BombObjectClass : public TimedObjectClass
{
public:
  BombObjectClass(int x, int y) : TimedObjectClass(x, y, Bomb, 150, ' ', FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
  {
    Maze[YPos][XPos] = Bomb;
    DrawMe(true);
  }
  virtual bool Trigger()
  {
    DrawMe(true);
    return TimedObjectClass::Trigger();
  }

  virtual ~BombObjectClass()
  {
    Maze[YPos][XPos] = Free;
  }
private:
  virtual void DrawMe(bool on)
  {
    OutCharXY(XPos, YPos, on ? (char )(Counter & 1 ? 149 : 162) : ' ', Attrib);
  }
};

class ManObjectClass : public ObjectBaseClass
{
public:
  ManObjectClass(int x, int y) : ObjectBaseClass(x, y, 10, Man)
  {
    DrawMe(true);
    Maze[YPos][XPos] = Man;
    NumBombs = 2;
    Energy = 60;
  }
  virtual ~ManObjectClass()
  {
    DrawMe(false);
    Maze[YPos][XPos] = Free;
    ShowStatus(Energy, NumBombs);
  }
  virtual bool Trigger()
  {
    int dx = 0, dy = 0;
    if(IsKeyPressed(VK_UP) && Maze[YPos - 1][XPos] != Wall)
    {
      dy = -1;
    }
    else if(IsKeyPressed(VK_DOWN) && Maze[YPos + 1][XPos] != Wall)
    {
      dy = 1;
    }
    else if(IsKeyPressed(VK_LEFT) && Maze[YPos][XPos - 1] != Wall)
    {
      dx = -1;
    }
    else if(IsKeyPressed(VK_RIGHT) && Maze[YPos][XPos + 1] != Wall)
    {
      dx = 1;
    }
    if(dx || dy)
    {
      DrawMe(false);
      Maze[YPos][XPos] = Free;
      if(IsKeyPressed(VK_SPACE) && NumBombs)
      {
        NumBombs--;
        ObjectList.Add(new BombObjectClass(XPos, YPos));
      }
      XPos += dx;
      YPos += dy;
      if(Maze[YPos][XPos] == BombTreasure)
      {
         ObjectList.RemoveObject(XPos, YPos, BombTreasure);
         NumBombs++;
      }
      else if(Maze[YPos][XPos] == Dot)
      {
         ObjectList.RemoveObject(XPos, YPos, Dot);
         Energy++;
      }
      else if(Maze[YPos][XPos] == Bomb)
      {
        ObjectList.RemoveObject(XPos, YPos, Bomb);
        Energy -= 20;
      }
      else if(Maze[YPos][XPos] == Sharp)
      {
        ObjectList.RemoveObject(XPos, YPos, Sharp);
        Energy -= 50;
      }
      Maze[YPos][XPos] = Man;
      DrawMe(true);
    }
    ShowStatus(Energy, NumBombs);
    return Energy > 0;
  }
  void KilledSharp()
  {
    Energy += 10;
    ShowStatus(Energy, NumBombs);
  }
  void HitSharp()
  {
     Energy -= 50;
     Maze[YPos][XPos] = Man;
     DrawMe(true);
     ShowStatus(Energy, NumBombs);
  }
private:
  void DrawMe(bool on)
  {
    OutCharXY(XPos, YPos, on ? '@' : ' ', FOREGROUND_BLUE | FOREGROUND_INTENSITY);
  }
  int NumBombs;
  int Energy;
};

class SharpObjectClass : public ObjectBaseClass
{
public:
  SharpObjectClass(int x, int y, int level) : ObjectBaseClass(x, y, 3 + level, Sharp)
  {
    DrawMe(true);
    Counter = 0;
    Maze[YPos][XPos] = Sharp;
  }
  virtual ~SharpObjectClass()
  {
  }
  virtual bool Trigger(void);

private:
  static const Point Dir[4];
  unsigned int FindPath(int x, int y, unsigned int level, Point *Path, unsigned int &MinDist);
  unsigned int Counter;
  unsigned int Shadow[Y_SIZE][X_SIZE];
  void DrawMe(bool on)
  {
    OutCharXY(XPos, YPos, on ? '#' : ' ', FOREGROUND_RED | FOREGROUND_INTENSITY);
  }
};

class BombTreasureObjectClass : public TimedObjectClass
{
public:
  BombTreasureObjectClass(int x, int y) : TimedObjectClass(x, y, BombTreasure, 200, '!', FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY)
  {
     Maze[YPos][XPos] = BombTreasure;
  }
  virtual ~BombTreasureObjectClass()
  {
    Maze[YPos][XPos] = Free;
  }
};

const Point SharpObjectClass::Dir[4] = {Point(-1, 0), Point(1, 0), Point(0, -1), Point(0, 1)};

unsigned int SharpObjectClass::FindPath(int x, int y, unsigned int level, Point *BestPath, unsigned int &MinDist)
{
  level++;
  if(Shadow[y][x] <= level || level >= MAX_DEPTH)
    return UINT_MAX;
  else
    Shadow[y][x] = level;

  unsigned int Max[4] = {UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX};
  int i;
  for(i = 0; i < 4; i++)
  {
    if(Maze[y + Dir[i].y][x + Dir[i].x] != Wall && Maze[y + Dir[i].y][x + Dir[i].x] != Man && Maze[y + Dir[i].y][x + Dir[i].x] != Sharp)
    {
      Max[i] = FindPath(x + Dir[i].x, y + Dir[i].y, level, BestPath, MinDist);
    }
    else if(Maze[y + Dir[i].y][x + Dir[i].x] == Man)
    {
      if(level < MinDist)
      { // The shortest path up to now!
        MinDist = level;
        BestPath[level].x = x + Dir[i].x;
        BestPath[level].y = y + Dir[i].y;
      }
      Max[i] = level;
    }
  }

  unsigned int n, m = UINT_MAX;
  for(n = 0; n < 4; n++)
    if(Max[n] < m)
    {
      m = Max[n];
    }
  if(m == MinDist)
  {
    BestPath[level - 1].x = x;
    BestPath[level - 1].y = y;
  }
  return m;
}

bool SharpObjectClass::Trigger(void)
{
  memset(Shadow, 0xFF, sizeof(Shadow));
  Point MinPath[MAX_DEPTH*2];
  unsigned int MinDist = UINT_MAX;
  if(FindPath(XPos, YPos, 0, MinPath, MinDist) > MAX_DEPTH)
     return true;

  Maze[YPos][XPos] = Free;
  DrawMe(false);
  XPos = MinPath[1].x;
  YPos = MinPath[1].y;
  if(Maze[YPos][XPos] == Bomb)
  {
    ObjectList.RemoveObject(XPos, YPos, Bomb);
    ManObjectClass *Man = ObjectList.FindMan();
    if(Man)
      Man->KilledSharp();
    return false;
  }
  else if(Maze[YPos][XPos] == BombTreasure)
  {
    ObjectList.RemoveObject(XPos, YPos, BombTreasure);
  }
  else if(Maze[YPos][XPos] == Dot)
  {
    ObjectList.RemoveObject(XPos, YPos, Dot);
  }
  else if(Maze[YPos][XPos] == Man)
  {
    ManObjectClass *Man = ObjectList.FindMan();
    if(Man)
      Man->HitSharp();
    return false;
  }
  Maze[YPos][XPos] = Sharp;
  DrawMe(true);
  return true;
}

class DotObjectClass : public TimedObjectClass
{
public:
  DotObjectClass(int x, int y) : TimedObjectClass(x, y, Dot, 200, '.', FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY)
  {
    Maze[y][x] = Dot;
  }
};

Point GetFreeSpot()
{
  Point p;
  do
  {
    p.x = Random(X_SIZE);
    p.y = Random(Y_SIZE);
  }
  while(Maze[p.y][p.x] != Free);
  return p;
}

Point GetSharpPoint(bool right)
{
  return Point(right ? X_SIZE - 2 : 1, 1);
}

int main(void)
{
  StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
  StdIn = GetStdHandle(STD_INPUT_HANDLE);
  if(GetYSize() < 25 || GetXSize() < 60)
  {
     printf("The Window is to small, you have to increase the size of the window,n I need at least 30 linesn");
     GetChar();
     return EXIT_FAILURE;
  }
  YOffset = GetYOffset() + 1;
  ClearScreen();
  Intro();
  ShowCaret(false);
  ShowStatus(0, 0);
  srand(time(0));

  int y, x;
  for(y = 0; y < Y_SIZE; y++)
  {
    for(x = 0; x < X_SIZE; x++)
    {
      if(Maze[y][x] == 1)
        OutCharXY(x, y, 0xB0, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
      else
        OutCharXY(x, y, ' ', FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    }
  }

  ObjectList.Add(new ManObjectClass(1, 1));
  ObjectList.Add(new SharpObjectClass(21, 1, 0));
  bool Run;
  int N = 3;

  do
  {
    Run = ObjectList.Trigger();
    if(Run)
    {
       if(++Score%(FREQ*50) == 0)
         N++;

      if(!Random(FREQ*5))
      {
        Point p = GetFreeSpot();
        ObjectList.Add(new BombTreasureObjectClass(p.x, p.y));
      }

      if(ObjectList.GetNumSharp() <= ((N - 3)/2) ||
         (ObjectList.GetNumSharp() < N && !Random(35*FREQ))  ||
         !Random(75*FREQ))
      {
        ManObjectClass *Man = ObjectList.FindMan();
        if(Man)
        { // If man is on the left half, create the sharp on the right half
          Point p = GetSharpPoint(Man->GetXPos() < X_SIZE/2);
          ObjectList.Add(new SharpObjectClass(p.x, p.y, N - 3));
        }
      }
      if(!Random(2*FREQ))
      {
        Point p = GetFreeSpot();
        ObjectList.Add(new DotObjectClass(p.x, p.y));
      }
    }
    if(Run)
      Sleep(1000/FREQ);
  }
  while(Run && !IsKeyPressed('Q'));
  while(KeyHit())
    GetChar();
  ShowCaret(true);
  ClearScreen();
  if(!Run)
  {
    printf("Your score was %dn", Score);
    HighScoreClass HighScore("HighScore.dat");
    HighScore.CheckInsert(Score);
    HighScore.PrintHighScore();
  }
  return 0;
}

void GotoXY(int x, int y)
{
  COORD c;
  c.X = (short )x;
  c.Y = (short )y;
  SetConsoleCursorPosition(StdOut, c);
}

void GetXY(int &x, int &y)
{
  CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
  GetConsoleScreenBufferInfo(StdOut, &ConsoleScreenBufferInfo);
  x = ConsoleScreenBufferInfo.dwCursorPosition.X;
  y = ConsoleScreenBufferInfo.dwCursorPosition.Y;
}

int GetYOffset()
{
  CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
  GetConsoleScreenBufferInfo(StdOut, &ConsoleScreenBufferInfo);
  return ConsoleScreenBufferInfo.srWindow.Top;
}

int GetYSize()
{
  CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
  GetConsoleScreenBufferInfo(StdOut, &ConsoleScreenBufferInfo);
  return ConsoleScreenBufferInfo.srWindow.Bottom - ConsoleScreenBufferInfo.srWindow.Top + 1;
}

int GetXSize()
{
  CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
  GetConsoleScreenBufferInfo(StdOut, &ConsoleScreenBufferInfo);
  return ConsoleScreenBufferInfo.srWindow.Right - ConsoleScreenBufferInfo.srWindow.Left + 1;
}

WORD GetChar()
{
  DWORD NumEventsRead;
  INPUT_RECORD InputRecord;

  while(1)
  {
    if(!ReadConsoleInput(StdIn, &InputRecord, 1, &NumEventsRead))
      return 0;
    if(InputRecord.EventType & KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown)
    {
      if(InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_CONTROL &&
         InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_MENU  &&
         InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_SHIFT)
      {
        return InputRecord.Event.KeyEvent.wVirtualKeyCode;
      }
    }
  }
}

BOOL KeyHit()
{
  DWORD NumEvents, NumEventsRead;
  INPUT_RECORD *InputRecord;
  DWORD i;
  GetNumberOfConsoleInputEvents(StdIn, &NumEvents);

  InputRecord = (INPUT_RECORD *)malloc(sizeof(INPUT_RECORD)*NumEvents);
  PeekConsoleInput(StdIn, InputRecord, NumEvents, &NumEventsRead);

  for(i = 0; i < NumEventsRead; i++)
  {
    if(InputRecord[i].EventType & KEY_EVENT && InputRecord[i].Event.KeyEvent.bKeyDown)
    {
      if(InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_CONTROL &&
         InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_MENU  &&
         InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_SHIFT)
      {
        free(InputRecord);
        return TRUE;
      }
    }
  }
  free(InputRecord);
  return FALSE;
}

void OutCharXY(int x, int y, char ch, WORD attr)
{
  COORD Coord;
  Coord.X = short(x);
  Coord.Y = short(y + YOffset);
  DWORD NumWritten;
  WriteConsoleOutputCharacter(StdOut, &ch, 1, Coord, &NumWritten);
  WriteConsoleOutputAttribute(StdOut, &attr, 1, Coord, &NumWritten);
}

BOOL IsKeyPressed(int Key)
{
  return GetAsyncKeyState(Key) & 0x8000 ? TRUE : FALSE;
}

void ShowCaret(BOOL on)
{
  CONSOLE_CURSOR_INFO ConsoleCursorInfo;
  GetConsoleCursorInfo(StdOut, &ConsoleCursorInfo);
  ConsoleCursorInfo.bVisible = on;
  SetConsoleCursorInfo(StdOut, &ConsoleCursorInfo);
}

void Intro()
{
  GotoXY(0, YOffset);
  printf("You are the @ trying to escape from the dreadful #'sn");
  printf("Collect a ! to get a bumb, . to get eneryn");
  printf("Drop a bomb by hitting space WHILE moving, to kill a #n");
  printf("Hit any key to start:");
  fflush(stdout);
  GetChar();
  ClearScreen();
}

void ClearScreen()
{
  short i;
  CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo;
  GetConsoleScreenBufferInfo(StdOut, &ConsoleScreenBufferInfo);
  SMALL_RECT Pos = ConsoleScreenBufferInfo.srWindow;

  DWORD D;

  for(i = Pos.Top; i <= Pos.Bottom; i++)
  {
    COORD C;
    C.X = Pos.Left;
    C.Y = i;
    FillConsoleOutputCharacter(StdOut, ' ', Pos.Right - Pos.Left, C, &D);
    FillConsoleOutputAttribute(StdOut, 0, Pos.Right - Pos.Left, C, &D);
  }
  GotoXY(0, 0);
}