--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/snake/snake.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,473 @@
+// snake.cpp
+//
+// Copyright (c) 2009 - 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#include <fshell/ioutils.h>
+#include <e32math.h>
+
+using namespace IoUtils;
+
+_LIT(KSnakeSeg, "*");
+_LIT(KBait, "$");
+
+class CKeyWatcher;
+
+class CCmdSnake : public CCommandBase
+ {
+public:
+ static CCommandBase* NewLC();
+ ~CCmdSnake();
+
+ void Up();
+ void Down();
+ void Left();
+ void Right();
+ void Exit();
+ void OnTimer();
+private:
+ CCmdSnake();
+private: // From CCommandBase.
+ virtual const TDesC& Name() const;
+ virtual void DoRunL();
+ virtual void ArgumentsL(RCommandArgumentList& aArguments);
+ virtual void OptionsL(RCommandOptionList& aOptions);
+
+ void ConstructL();
+ void InitBoardL();
+ void SetBoard(TInt aX, TInt aY, TBool aSet);
+ TBool GetBoard(TInt aX, TInt aY);
+ void DrawBoardL();
+ void InitSnakeL(TInt aLen, TPoint aPos);
+ void DrawSnakeL();
+ void DrawScore();
+ void PlaceBait();
+ void Grow();
+
+ void Dead();
+private:
+ RIoConsoleWriteHandle iCons;
+ RIoConsoleReadHandle iConsIn;
+ TSize iBoardSize;
+ RArray<TPoint> iSnake;
+ TInt iSnakeHead;
+ RArray<TUint32> iBoard;
+ TInt iBoardWidthWords;
+ TPoint iDirection;
+ TPoint iBait;
+ TInt iScore;
+ TInt iSpeed;
+
+ CKeyWatcher* iKeys;
+ CPeriodic* iTimer;
+ TInt64 iRandomSeed;
+ TBool iInCollisionGracePeriod;
+ };
+
+class CKeyWatcher : public CActive
+ {
+public:
+ CKeyWatcher(CCmdSnake& aOwner, RIoConsoleReadHandle& aCons);
+ ~CKeyWatcher();
+ void Request();
+
+private:
+ virtual void RunL();
+ virtual void DoCancel();
+private:
+ CCmdSnake& iOwner;
+ RIoConsoleReadHandle& iCons;
+ };
+
+
+CCommandBase* CCmdSnake::NewLC()
+ {
+ CCmdSnake* self = new(ELeave) CCmdSnake();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+CCmdSnake::~CCmdSnake()
+ {
+ delete iKeys;
+ delete iTimer;
+ iSnake.Close();
+ iBoard.Close();
+ }
+
+CCmdSnake::CCmdSnake()
+ : CCommandBase(EManualComplete), iSpeed(50000)
+ {
+ }
+
+const TDesC& CCmdSnake::Name() const
+ {
+ _LIT(KName, "snake");
+ return KName;
+ }
+
+TInt OnTimerS(TAny* aSnake)
+ {
+ ((CCmdSnake*)aSnake)->OnTimer();
+ return KErrNone;
+ }
+
+void CCmdSnake::DoRunL()
+ {
+ if (!Stdout().AttachedToConsole())
+ {
+ LeaveIfErr(KErrNotSupported, _L("Stdout must be connected to a console"));
+ }
+ iCons = Stdout();
+ if (!Stdin().AttachedToConsole())
+ {
+ LeaveIfErr(KErrNotSupported, _L("Stdin must be connected to a console"));
+ }
+ iConsIn = Stdin();
+ iKeys = new(ELeave)CKeyWatcher(*this, iConsIn);
+ iCons.SetCursorHeight(0);
+
+ User::LeaveIfError(iCons.GetScreenSize(iBoardSize));
+ iBoardSize.iHeight--; // space for status bar, plus to ensure that the console doesn't scroll when we draw the bottom line
+ iBoardWidthWords = iBoardSize.iWidth / 32;
+ if (iBoardSize.iWidth % 32) ++iBoardWidthWords;
+ InitBoardL();
+
+ DrawBoardL();
+
+ TPoint centre;
+ centre.iX = iBoardSize.iWidth / 2;
+ centre.iY = iBoardSize.iHeight / 2;
+ InitSnakeL(10, centre);
+ DrawSnakeL();
+ PlaceBait();
+ DrawScore();
+
+ iTimer = CPeriodic::NewL(CActive::EPriorityStandard);
+ iTimer->Start(iSpeed, iSpeed, TCallBack(OnTimerS, this));
+ }
+
+void CCmdSnake::ArgumentsL(RCommandArgumentList& /*aArguments*/)
+ {
+ }
+
+void CCmdSnake::OptionsL(RCommandOptionList& aOptions)
+ {
+ _LIT(KOptSpeed, "speed");
+ aOptions.AppendIntL(iSpeed, KOptSpeed);
+ }
+
+void CCmdSnake::ConstructL()
+ {
+ BaseConstructL();
+ TTime now;
+ now.HomeTime();
+ iRandomSeed = now.Int64();
+ }
+
+void CCmdSnake::InitBoardL()
+ {
+ // first, zero the whole board
+ iBoard.Reset();
+ for (TInt y=0; y<iBoardSize.iHeight; ++y)
+ {
+ for (TInt x=0; x<iBoardWidthWords; ++x)
+ {
+ iBoard.AppendL(0);
+ }
+ }
+ // draw a box around the edge
+ for (TInt i=0; i<iBoardSize.iWidth; ++i)
+ {
+ SetBoard(i, 0, ETrue);
+ SetBoard(i, iBoardSize.iHeight-1, ETrue);
+ }
+ for (TInt i=0; i<iBoardSize.iHeight; ++i)
+ {
+ SetBoard(0, i, ETrue);
+ SetBoard(iBoardSize.iWidth-1, i, ETrue);
+ }
+ }
+
+void CCmdSnake::SetBoard(TInt aX, TInt aY, TBool aSet)
+ {
+ TInt word = (aY * iBoardWidthWords) + (aX / 32);
+ TInt bit = 1 << (aX % 32);
+ if (aSet)
+ {
+ iBoard[word] |= bit;
+ }
+ else
+ {
+ iBoard[word] &= (~bit);
+ }
+ }
+
+TBool CCmdSnake::GetBoard(TInt aX, TInt aY)
+ {
+ TInt word = (aY * iBoardWidthWords) + (aX / 32);
+ TInt bit = 1 << (aX % 32);
+ return iBoard[word] & bit;
+ }
+
+void CCmdSnake::DrawBoardL()
+ {
+ User::LeaveIfError(iCons.ClearScreen());
+
+ RBuf line;
+ line.CreateMaxL(iBoardSize.iWidth);
+ CleanupClosePushL(line);
+
+ for (TInt y=0; y<iBoardSize.iHeight; ++y)
+ {
+ for (TInt x=0; x<iBoardSize.iWidth; ++x)
+ {
+ line[x] = GetBoard(x,y) ? '#' : ' ';
+ }
+ User::LeaveIfError(iCons.SetCursorPosAbs(TPoint(0, y)));
+ User::LeaveIfError(iCons.Write(line));
+ }
+
+
+ CleanupStack::PopAndDestroy();
+ }
+
+void CCmdSnake::InitSnakeL(TInt aLen, TPoint aPos)
+ {
+ iSnake.Reset();
+ for (TInt i=0; i<aLen; ++i)
+ {
+ iSnake.AppendL(aPos);
+ }
+ iSnakeHead = 0;
+ }
+
+void CCmdSnake::DrawSnakeL()
+ {
+ for (TInt i=0; i<iSnake.Count(); ++i)
+ {
+ User::LeaveIfError(iCons.SetCursorPosAbs(iSnake[i]));
+ User::LeaveIfError(iCons.Write(KSnakeSeg));
+ }
+ }
+
+void CCmdSnake::Up()
+ {
+ if (iDirection.iY != 1)
+ {
+ iDirection.SetXY(0, -1);
+ }
+ }
+
+void CCmdSnake::Down()
+ {
+ if (iDirection.iY != -1)
+ {
+ iDirection.SetXY(0, 1);
+ }
+ }
+
+void CCmdSnake::Left()
+ {
+ if (iDirection.iX != 1)
+ {
+ iDirection.SetXY(-1, 0);
+ }
+ }
+
+void CCmdSnake::Right()
+ {
+ if (iDirection.iX != -1)
+ {
+ iDirection.SetXY(1, 0);
+ }
+ }
+
+_LIT(KQuitFmt, "Quit; score: %d\n");
+
+void CCmdSnake::Exit()
+ {
+ iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
+ iCons.SetCursorHeight(20);
+ TBuf<0x20> buf;
+ buf.AppendFormat(KQuitFmt, iScore);
+ iCons.Write(buf);
+ Complete(iScore);
+ }
+
+_LIT(KDeadFmt, "Game over; score: %d\n");
+
+void CCmdSnake::Dead()
+ {
+ iKeys->Cancel();
+ iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
+ iCons.SetCursorHeight(20);
+ TBuf<0x20> buf;
+ buf.AppendFormat(KDeadFmt, iScore);
+ iCons.Write(buf);
+ Complete(iScore);
+ }
+
+void CCmdSnake::OnTimer()
+ {
+ iKeys->Request();
+ if (!(iDirection.iX || iDirection.iY)) return;
+ TPoint newHead = iSnake[iSnakeHead] + iDirection + iBoardSize;
+ newHead.iX %= iBoardSize.iWidth;
+ newHead.iY %= iBoardSize.iHeight;
+
+ // check for obstacles
+ if (GetBoard(newHead.iX, newHead.iY))
+ {
+ if (iInCollisionGracePeriod)
+ {
+ Dead();
+ }
+ else
+ {
+ iInCollisionGracePeriod = ETrue;
+ }
+ return;
+ }
+ iInCollisionGracePeriod = EFalse;
+ for (TInt i=0; i<iSnake.Count(); ++i)
+ {
+ if (newHead == iSnake[i])
+ {
+ Dead();
+ return;
+ }
+ }
+
+ TInt tail = (iSnakeHead+1) % iSnake.Count();
+ // overwrite the tail if it's not in the same position as the segment before it
+ if (iSnake[tail] != iSnake[(tail+1)%iSnake.Count()])
+ {
+ iCons.SetCursorPosAbs(iSnake[tail]);
+ iCons.Write(_L(" "));
+ }
+ iCons.SetCursorPosAbs(newHead);
+ iCons.Write(KSnakeSeg);
+ iSnakeHead = (iSnakeHead+1) % iSnake.Count();
+ iSnake[iSnakeHead] = newHead;
+
+ if (iSnake[iSnakeHead] == iBait)
+ {
+ // Yummy!
+ iScore++;
+ DrawScore();
+ PlaceBait();
+ Grow();
+ }
+ }
+
+_LIT(KScoreFormat, "Score: %d");
+
+void CCmdSnake::DrawScore()
+ {
+ iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
+ TBuf<0x20> score;
+ score.AppendFormat(KScoreFormat, iScore);
+ iCons.Write(score);
+ }
+
+void CCmdSnake::PlaceBait()
+ {
+ TBool ok;
+ do
+ {
+ ok = ETrue;
+ iBait.iX = Math::Rand(iRandomSeed) % iBoardSize.iWidth;
+ iBait.iY = Math::Rand(iRandomSeed) % iBoardSize.iHeight;
+
+ if (GetBoard(iBait.iX, iBait.iY)) ok = EFalse;
+ if (ok)
+ {
+ for (TInt i=0; i<iSnake.Count(); ++i)
+ {
+ if (iSnake[i] == iBait) ok = EFalse;
+ }
+ }
+ } while (!ok);
+ iCons.SetCursorPosAbs(iBait);
+ iCons.Write(KBait);
+ }
+
+void CCmdSnake::Grow()
+ {
+ TInt tail = (iSnakeHead+1)% iSnake.Count();
+ iSnake.Insert(iSnake[tail], tail);
+ if (tail < iSnakeHead) iSnakeHead++;
+ }
+
+
+
+EXE_BOILER_PLATE(CCmdSnake)
+
+
+//______________________________________________________________________________
+// CKeyWatcher
+CKeyWatcher::CKeyWatcher(CCmdSnake& aOwner, RIoConsoleReadHandle& aCons)
+ : CActive(EPriorityHigh), iOwner(aOwner), iCons(aCons)
+ {
+ CActiveScheduler::Add(this);
+ Request();
+ }
+
+CKeyWatcher::~CKeyWatcher()
+ {
+ Cancel();
+ }
+
+void CKeyWatcher::RunL()
+ {
+ User::LeaveIfError(iStatus.Int());
+ TKeyCode key = (TKeyCode)iCons.KeyCode();
+ switch (key)
+ {
+ case EKeyUpArrow:
+ case '2':
+ iOwner.Up();
+ break;
+ case EKeyDownArrow:
+ case '8':
+ iOwner.Down();
+ break;
+ case EKeyLeftArrow:
+ case '4':
+ iOwner.Left();
+ break;
+ case EKeyRightArrow:
+ case '6':
+ iOwner.Right();
+ break;
+ case EKeyEscape:
+ case EKeyBackspace:
+ iOwner.Exit();
+ break;
+ default:
+ break;
+ }
+ }
+
+void CKeyWatcher::DoCancel()
+ {
+ iCons.WaitForKeyCancel();
+ }
+
+void CKeyWatcher::Request()
+ {
+ if (!IsActive())
+ {
+ iCons.WaitForKey(iStatus);
+ SetActive();
+ }
+ }