commands/snake/snake.cpp
changeset 0 7f656887cf89
child 83 2a78c4ff2eab
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // snake.cpp
       
     2 // 
       
     3 // Copyright (c) 2009 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include <fshell/ioutils.h>
       
    14 #include <e32math.h>
       
    15 
       
    16 using namespace IoUtils;
       
    17 
       
    18 _LIT(KSnakeSeg, "*");
       
    19 _LIT(KBait, "$");
       
    20 
       
    21 class CKeyWatcher;
       
    22 
       
    23 class CCmdSnake : public CCommandBase
       
    24 	{
       
    25 public:
       
    26 	static CCommandBase* NewLC();
       
    27 	~CCmdSnake();
       
    28 	
       
    29 	void Up();
       
    30 	void Down();
       
    31 	void Left();
       
    32 	void Right();
       
    33 	void Exit();
       
    34 	void OnTimer();
       
    35 private:
       
    36 	CCmdSnake();
       
    37 private: // From CCommandBase.
       
    38 	virtual const TDesC& Name() const;
       
    39 	virtual void DoRunL();
       
    40 	virtual void ArgumentsL(RCommandArgumentList& aArguments);
       
    41 	virtual void OptionsL(RCommandOptionList& aOptions);
       
    42 	
       
    43 	void ConstructL();
       
    44 	void InitBoardL();
       
    45 	void SetBoard(TInt aX, TInt aY, TBool aSet);
       
    46 	TBool GetBoard(TInt aX, TInt aY);
       
    47 	void DrawBoardL();
       
    48 	void InitSnakeL(TInt aLen, TPoint aPos);
       
    49 	void DrawSnakeL();
       
    50 	void DrawScore();
       
    51 	void PlaceBait();
       
    52 	void Grow();
       
    53 	
       
    54 	void Dead();
       
    55 private:
       
    56 	RIoConsoleWriteHandle iCons;
       
    57 	RIoConsoleReadHandle iConsIn;
       
    58 	TSize iBoardSize;
       
    59 	RArray<TPoint> iSnake;
       
    60 	TInt iSnakeHead;
       
    61 	RArray<TUint32> iBoard;
       
    62 	TInt iBoardWidthWords;
       
    63 	TPoint iDirection;
       
    64 	TPoint iBait;
       
    65 	TInt iScore;
       
    66 	TInt iSpeed;
       
    67 	
       
    68 	CKeyWatcher* iKeys;
       
    69 	CPeriodic* iTimer;
       
    70 	TInt64 iRandomSeed;
       
    71 	TBool iInCollisionGracePeriod;
       
    72 	};
       
    73 
       
    74 class CKeyWatcher : public CActive
       
    75 	{
       
    76 public:
       
    77 	CKeyWatcher(CCmdSnake& aOwner, RIoConsoleReadHandle& aCons);
       
    78 	~CKeyWatcher();
       
    79 	void Request();
       
    80 
       
    81 private:
       
    82 	virtual void RunL();
       
    83 	virtual void DoCancel();
       
    84 private:
       
    85 	CCmdSnake& iOwner;
       
    86 	RIoConsoleReadHandle& iCons;
       
    87 	};
       
    88 
       
    89 
       
    90 CCommandBase* CCmdSnake::NewLC()
       
    91 	{
       
    92 	CCmdSnake* self = new(ELeave) CCmdSnake();
       
    93 	CleanupStack::PushL(self);
       
    94 	self->ConstructL();
       
    95 	return self;
       
    96 	}
       
    97 
       
    98 CCmdSnake::~CCmdSnake()
       
    99 	{
       
   100 	delete iKeys;
       
   101 	delete iTimer;
       
   102 	iSnake.Close();
       
   103 	iBoard.Close();
       
   104 	}
       
   105 
       
   106 CCmdSnake::CCmdSnake()
       
   107 	: CCommandBase(EManualComplete), iSpeed(50000)
       
   108 	{
       
   109 	}
       
   110 
       
   111 const TDesC& CCmdSnake::Name() const
       
   112 	{
       
   113 	_LIT(KName, "snake");	
       
   114 	return KName;
       
   115 	}
       
   116 	
       
   117 TInt OnTimerS(TAny* aSnake)
       
   118 	{
       
   119 	((CCmdSnake*)aSnake)->OnTimer();
       
   120 	return KErrNone;
       
   121 	}
       
   122 
       
   123 void CCmdSnake::DoRunL()
       
   124 	{
       
   125 	if (!Stdout().AttachedToConsole())
       
   126 		{
       
   127 		LeaveIfErr(KErrNotSupported, _L("Stdout must be connected to a console"));
       
   128 		}
       
   129 	iCons = Stdout();
       
   130 	if (!Stdin().AttachedToConsole())
       
   131 		{
       
   132 		LeaveIfErr(KErrNotSupported, _L("Stdin must be connected to a console"));
       
   133 		}
       
   134 	iConsIn = Stdin();
       
   135 	iKeys = new(ELeave)CKeyWatcher(*this, iConsIn);
       
   136 	iCons.SetCursorHeight(0);
       
   137 	
       
   138 	User::LeaveIfError(iCons.GetScreenSize(iBoardSize));
       
   139 	iBoardSize.iHeight--; // space for status bar, plus to ensure that the console doesn't scroll when we draw the bottom line
       
   140 	iBoardWidthWords = iBoardSize.iWidth / 32;
       
   141 	if (iBoardSize.iWidth % 32) ++iBoardWidthWords;
       
   142 	InitBoardL();
       
   143 	
       
   144 	DrawBoardL();
       
   145 	
       
   146 	TPoint centre;
       
   147 	centre.iX = iBoardSize.iWidth / 2;
       
   148 	centre.iY = iBoardSize.iHeight / 2;
       
   149 	InitSnakeL(10, centre);	
       
   150 	DrawSnakeL();
       
   151 	PlaceBait();
       
   152 	DrawScore();
       
   153 	
       
   154 	iTimer = CPeriodic::NewL(CActive::EPriorityStandard);
       
   155 	iTimer->Start(iSpeed, iSpeed, TCallBack(OnTimerS, this));
       
   156 	}
       
   157 
       
   158 void CCmdSnake::ArgumentsL(RCommandArgumentList& /*aArguments*/)
       
   159 	{
       
   160 	}
       
   161 
       
   162 void CCmdSnake::OptionsL(RCommandOptionList& aOptions)
       
   163 	{
       
   164 	_LIT(KOptSpeed, "speed");
       
   165 	aOptions.AppendIntL(iSpeed, KOptSpeed);
       
   166 	}
       
   167 	
       
   168 void CCmdSnake::ConstructL()
       
   169 	{
       
   170 	BaseConstructL();
       
   171 	TTime now;
       
   172 	now.HomeTime();
       
   173 	iRandomSeed = now.Int64();
       
   174 	}
       
   175 
       
   176 void CCmdSnake::InitBoardL()
       
   177 	{
       
   178 	// first, zero the whole board
       
   179 	iBoard.Reset();
       
   180 	for (TInt y=0; y<iBoardSize.iHeight; ++y)
       
   181 		{
       
   182 		for (TInt x=0; x<iBoardWidthWords; ++x)
       
   183 			{
       
   184 			iBoard.AppendL(0);
       
   185 			}
       
   186 		}
       
   187 	// draw a box around the edge
       
   188 	for (TInt i=0; i<iBoardSize.iWidth; ++i)
       
   189 		{
       
   190 		SetBoard(i, 0, ETrue);
       
   191 		SetBoard(i, iBoardSize.iHeight-1, ETrue);
       
   192 		}
       
   193 	for (TInt i=0; i<iBoardSize.iHeight; ++i)
       
   194 		{
       
   195 		SetBoard(0, i, ETrue);
       
   196 		SetBoard(iBoardSize.iWidth-1, i, ETrue);
       
   197 		}
       
   198 	}
       
   199 	
       
   200 void CCmdSnake::SetBoard(TInt aX, TInt aY, TBool aSet)
       
   201 	{
       
   202 	TInt word = (aY * iBoardWidthWords) + (aX / 32);
       
   203 	TInt bit = 1 << (aX % 32);
       
   204 	if (aSet)
       
   205 		{
       
   206 		iBoard[word] |= bit;
       
   207 		}
       
   208 	else
       
   209 		{
       
   210 		iBoard[word] &= (~bit);
       
   211 		}
       
   212 	}
       
   213 	
       
   214 TBool CCmdSnake::GetBoard(TInt aX, TInt aY)
       
   215 	{
       
   216 	TInt word = (aY * iBoardWidthWords) + (aX / 32);
       
   217 	TInt bit = 1 << (aX % 32);
       
   218 	return iBoard[word] & bit;
       
   219 	}
       
   220 	
       
   221 void CCmdSnake::DrawBoardL()
       
   222 	{
       
   223 	User::LeaveIfError(iCons.ClearScreen());
       
   224 	
       
   225 	RBuf line;
       
   226 	line.CreateMaxL(iBoardSize.iWidth);
       
   227 	CleanupClosePushL(line);
       
   228 	
       
   229 	for (TInt y=0; y<iBoardSize.iHeight; ++y)
       
   230 		{
       
   231 		for (TInt x=0; x<iBoardSize.iWidth; ++x)
       
   232 			{
       
   233 			line[x] = GetBoard(x,y) ? '#' : ' ';
       
   234 			}
       
   235 		User::LeaveIfError(iCons.SetCursorPosAbs(TPoint(0, y)));
       
   236 		User::LeaveIfError(iCons.Write(line));
       
   237 		}
       
   238 	
       
   239 	
       
   240 	CleanupStack::PopAndDestroy();
       
   241 	}
       
   242 	
       
   243 void CCmdSnake::InitSnakeL(TInt aLen, TPoint aPos)
       
   244 	{
       
   245 	iSnake.Reset();
       
   246 	for (TInt i=0; i<aLen; ++i)
       
   247 		{
       
   248 		iSnake.AppendL(aPos);
       
   249 		}
       
   250 	iSnakeHead = 0;
       
   251 	}
       
   252 	
       
   253 void CCmdSnake::DrawSnakeL()
       
   254 	{
       
   255 	for (TInt i=0; i<iSnake.Count(); ++i)
       
   256 		{
       
   257 		User::LeaveIfError(iCons.SetCursorPosAbs(iSnake[i]));
       
   258 		User::LeaveIfError(iCons.Write(KSnakeSeg));
       
   259 		}
       
   260 	}
       
   261 
       
   262 void CCmdSnake::Up()
       
   263 	{
       
   264 	if (iDirection.iY != 1)
       
   265 		{
       
   266 		iDirection.SetXY(0, -1);
       
   267 		}
       
   268 	}
       
   269 	
       
   270 void CCmdSnake::Down()
       
   271 	{
       
   272 	if (iDirection.iY != -1)
       
   273 		{
       
   274 		iDirection.SetXY(0, 1);
       
   275 		}
       
   276 	}
       
   277 	
       
   278 void CCmdSnake::Left()
       
   279 	{
       
   280 	if (iDirection.iX != 1)
       
   281 		{
       
   282 		iDirection.SetXY(-1, 0);
       
   283 		}
       
   284 	}
       
   285 	
       
   286 void CCmdSnake::Right()
       
   287 	{
       
   288 	if (iDirection.iX != -1)
       
   289 		{
       
   290 		iDirection.SetXY(1, 0);
       
   291 		}
       
   292 	}
       
   293 	
       
   294 _LIT(KQuitFmt, "Quit; score: %d\n");
       
   295 
       
   296 void CCmdSnake::Exit()
       
   297 	{
       
   298 	iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
       
   299 	iCons.SetCursorHeight(20);
       
   300 	TBuf<0x20> buf;
       
   301 	buf.AppendFormat(KQuitFmt, iScore);
       
   302 	iCons.Write(buf);
       
   303 	Complete(iScore);
       
   304 	}
       
   305 	
       
   306 _LIT(KDeadFmt, "Game over; score: %d\n");
       
   307 
       
   308 void CCmdSnake::Dead()
       
   309 	{
       
   310 	iKeys->Cancel();
       
   311 	iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
       
   312 	iCons.SetCursorHeight(20);
       
   313 	TBuf<0x20> buf;
       
   314 	buf.AppendFormat(KDeadFmt, iScore);
       
   315 	iCons.Write(buf);
       
   316 	Complete(iScore);
       
   317 	}
       
   318 	
       
   319 void CCmdSnake::OnTimer()
       
   320 	{
       
   321 	iKeys->Request();
       
   322 	if (!(iDirection.iX || iDirection.iY)) return;
       
   323 	TPoint newHead = iSnake[iSnakeHead] + iDirection + iBoardSize;
       
   324 	newHead.iX %= iBoardSize.iWidth;
       
   325 	newHead.iY %= iBoardSize.iHeight;
       
   326 	
       
   327 	// check for obstacles
       
   328 	if (GetBoard(newHead.iX, newHead.iY))
       
   329 		{
       
   330 		if (iInCollisionGracePeriod)
       
   331 			{
       
   332 			Dead();
       
   333 			}
       
   334 		else
       
   335 			{
       
   336 			iInCollisionGracePeriod = ETrue;
       
   337 			}
       
   338 		return;
       
   339 		}
       
   340 	iInCollisionGracePeriod = EFalse;
       
   341 	for (TInt i=0; i<iSnake.Count(); ++i)
       
   342 		{
       
   343 		if (newHead == iSnake[i])
       
   344 			{
       
   345 			Dead();
       
   346 			return;
       
   347 			}
       
   348 		}
       
   349 	
       
   350 	TInt tail = (iSnakeHead+1) % iSnake.Count();
       
   351 	// overwrite the tail if it's not in the same position as the segment before it
       
   352 	if (iSnake[tail] != iSnake[(tail+1)%iSnake.Count()])
       
   353 		{
       
   354 		iCons.SetCursorPosAbs(iSnake[tail]);
       
   355 		iCons.Write(_L(" "));
       
   356 		}
       
   357 	iCons.SetCursorPosAbs(newHead);
       
   358 	iCons.Write(KSnakeSeg);
       
   359 	iSnakeHead = (iSnakeHead+1) % iSnake.Count();
       
   360 	iSnake[iSnakeHead] = newHead;
       
   361 	
       
   362 	if (iSnake[iSnakeHead] == iBait)
       
   363 		{
       
   364 		// Yummy!
       
   365 		iScore++;
       
   366 		DrawScore();
       
   367 		PlaceBait();
       
   368 		Grow();
       
   369 		}
       
   370 	}
       
   371 	
       
   372 _LIT(KScoreFormat, "Score: %d");
       
   373 	
       
   374 void CCmdSnake::DrawScore()
       
   375 	{
       
   376 	iCons.SetCursorPosAbs(TPoint(0, iBoardSize.iHeight));
       
   377 	TBuf<0x20> score;
       
   378 	score.AppendFormat(KScoreFormat, iScore);
       
   379 	iCons.Write(score);
       
   380 	}
       
   381 
       
   382 void CCmdSnake::PlaceBait()
       
   383 	{
       
   384 	TBool ok;
       
   385 	do
       
   386 		{
       
   387 		ok = ETrue;
       
   388 		iBait.iX = Math::Rand(iRandomSeed) % iBoardSize.iWidth;
       
   389 		iBait.iY = Math::Rand(iRandomSeed) % iBoardSize.iHeight;
       
   390 		
       
   391 		if (GetBoard(iBait.iX, iBait.iY)) ok = EFalse;
       
   392 		if (ok)
       
   393 			{
       
   394 			for (TInt i=0; i<iSnake.Count(); ++i)
       
   395 				{
       
   396 				if (iSnake[i] == iBait) ok = EFalse;
       
   397 				}
       
   398 			}
       
   399 		} while (!ok);
       
   400 	iCons.SetCursorPosAbs(iBait);
       
   401 	iCons.Write(KBait);
       
   402 	}
       
   403 
       
   404 void CCmdSnake::Grow()
       
   405 	{
       
   406 	TInt tail = (iSnakeHead+1)% iSnake.Count();
       
   407 	iSnake.Insert(iSnake[tail], tail);
       
   408 	if (tail < iSnakeHead) iSnakeHead++;
       
   409 	}
       
   410 
       
   411 
       
   412 
       
   413 EXE_BOILER_PLATE(CCmdSnake)
       
   414 
       
   415 
       
   416 //______________________________________________________________________________
       
   417 //						CKeyWatcher
       
   418 CKeyWatcher::CKeyWatcher(CCmdSnake& aOwner, RIoConsoleReadHandle& aCons)
       
   419 	: CActive(EPriorityHigh), iOwner(aOwner), iCons(aCons)
       
   420 	{
       
   421 	CActiveScheduler::Add(this);
       
   422 	Request();
       
   423 	}
       
   424 
       
   425 CKeyWatcher::~CKeyWatcher()
       
   426 	{
       
   427 	Cancel();
       
   428 	}
       
   429 
       
   430 void CKeyWatcher::RunL()
       
   431 	{
       
   432 	User::LeaveIfError(iStatus.Int());
       
   433 	TKeyCode key = (TKeyCode)iCons.KeyCode();
       
   434 	switch (key)
       
   435 		{
       
   436 	case EKeyUpArrow:
       
   437 	case '2':
       
   438 		iOwner.Up();
       
   439 		break;
       
   440 	case EKeyDownArrow:
       
   441 	case '8':
       
   442 		iOwner.Down();
       
   443 		break;
       
   444 	case EKeyLeftArrow:
       
   445 	case '4':
       
   446 		iOwner.Left();
       
   447 		break;
       
   448 	case EKeyRightArrow:
       
   449 	case '6':
       
   450 		iOwner.Right();
       
   451 		break;
       
   452 	case EKeyEscape:
       
   453 	case EKeyBackspace:
       
   454 		iOwner.Exit();
       
   455 		break;
       
   456 	default:
       
   457 		break;
       
   458 		}
       
   459 	}
       
   460 
       
   461 void CKeyWatcher::DoCancel()
       
   462 	{
       
   463 	iCons.WaitForKeyCancel();
       
   464 	}
       
   465 
       
   466 void CKeyWatcher::Request()
       
   467 	{
       
   468 	if (!IsActive())
       
   469 		{
       
   470 		iCons.WaitForKey(iStatus);
       
   471 		SetActive();
       
   472 		}
       
   473 	}