# TypeScript Adventure: Conquering The Final Boss

Welcome, brave TypeScript adventurers, to the ultimate challenge of our epic journey! 🐉 We've battled through functions, vanquished classes, and mastered advanced types.

Now, it's time to face the Final Boss: creating a Terminal Snake Game using everything we've learned. Let's build this game step by step, testing as we go!

## Setting Up Our Battleground

First, let's prepare our project:

```bash
mkdir typescript-snake
cd typescript-snake
npm init -y
npm install typescript @types/node --save-dev
npx tsc --init
```

Now, update your `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "es2017",
    "module": "CommonJS",
    "outDir": "dist",
    "strict": true
  }
}
```

## Step 1: Defining Our Game Types

Let's start by defining the core types for our game. Create a file `src/types.ts`:

```typescript
export interface Position {
  x: number;
  y: number;
}

export interface SnakeSegment extends Position {
  next?: SnakeSegment;
}

export interface Food extends Position {}

export type Direction = 'up' | 'down' | 'left' | 'right';

export interface GameState {
  snake: SnakeSegment;
  food: Food;
  direction: Direction;
  gridSize: number;
}
```

These types define the structure of our game elements. The `SnakeSegment` is a `linked list`, allowing us to easily add segments as the snake grows.

## Step 2: Creating the Initial Game State

Now, let's create our initial game state. Add this to `src/game.ts`:

```typescript
import { GameState, Position, Direction } from './types';

export function createInitialGameState(gridSize: number): GameState {
  return {
    snake: { x: Math.floor(gridSize / 2), y: Math.floor(gridSize / 2) },
    food: generateFood(gridSize),
    direction: 'right',
    gridSize
  };
}

function generateFood(gridSize: number): Position {
  return {
    x: Math.floor(Math.random() * gridSize),
    y: Math.floor(Math.random() * gridSize)
  };
}
```

This function creates our initial game state with the snake in the middle and food at a random position.

Let's test it! Create `src/index.ts`:

```typescript
import { createInitialGameState } from './game';

console.log(JSON.stringify(createInitialGameState(20), null, 2));
```

Now, compile and run:

```bash
npx tsc && node dist/index.js
```

You should see the initial game state logged to the console.

## Step 3: Moving the Snake

Let's implement snake movement. Add these functions to `src/game.ts`:

```typescript
export function updateGameState(state: GameState): GameState {
 const newHead = moveSnake(state.snake, state.direction);
  if (hasCollidedWithFood(newHead, state.food)) {
    return {
      ...state,
      snake: { ...newHead, next: state.snake },
      food: generateFood(state.gridSize)
    };
  }
  if (hasCollidedWithWall(newHead, state.gridSize) || hasCollidedWithSelf(newHead, state.snake)) {
    throw new Error('Game Over');
  }
  return {
    ...state,
    snake: { ...newHead, next: state.snake.next }
  };
}

function moveSnake(head: SnakeSegment, direction: Direction): Position {
  switch (direction) {
    case 'up': return { ...head, y: head.y - 1 };
    case 'down': return { ...head, y: head.y + 1 };
    case 'left': return { ...head, x: head.x - 1 };
    case 'right': return { ...head, x: head.x + 1 };
  }
}

function hasCollidedWithFood(head: Position, food: Position): boolean {
  return head.x === food.x && head.y === food.y;
}

function hasCollidedWithWall(head: Position, gridSize: number): boolean {
  return head.x < 0 || head.x >= gridSize || head.y < 0 || head.y >= gridSize;
}

function hasCollidedWithSelf(head: Position, snake: SnakeSegment): boolean {
  let current = snake.next;
  while (current) {
    if (head.x === current.x && head.y === current.y) return true;
    current = current.next;
  }
  return false;
}
```

These functions handle moving the snake, checking for collisions, and updating the game state.

Let's test it by updating `src/index.ts`:

```typescript
import { createInitialGameState, updateGameState } from './game';

let state = createInitialGameState(20);

console.log('Initial state:', JSON.stringify(state, null, 2));

for (let i = 0; i < 5; i++) {
  state = updateGameState(state);
  console.log(`State after move ${i + 1}:`, JSON.stringify(state, null, 2));
}
```

Compile and run again to see the snake move!

## Step 4: Rendering the Game

Now, let's visualize our game. Add this to `src/render.ts`:

```typescript
import { GameState, SnakeSegment } from './types';

export function renderGame(state: GameState): string {
  const grid = Array(state.gridSize).fill(null).map(() => 
    Array(state.gridSize).fill(' ')
  );
  let currentSegment: SnakeSegment | undefined = state.snake;
  while (currentSegment) {
    grid[currentSegment.y][currentSegment.x] = '█';
    currentSegment = currentSegment.next;
  }
  grid[state.food.y][state.food.x] = '●';
  return grid.map(row => row.join('')).join('\n');
}
```

This function creates a string representation of our game state.

Let's test it by updating `src/index.ts`:

```typescript
import { createInitialGameState, updateGameState } from './game';
import { renderGame } from './render';

let state = createInitialGameState(20);

console.log('Initial state:');
console.log(renderGame(state));

for (let i = 0; i < 5; i++) {
  state = updateGameState(state);
  console.log(`State after move ${i + 1}:`);
  console.log(renderGame(state));
}
```

Compile and run to see your snake move across the grid!

## Step 5: Adding User Input

Finally, let's make our game interactive. Create `src/input.ts`:

```typescript
import * as readline from 'readline';
import { Direction } from './types';

export function setupInput(onDirectionChange: (direction: Direction) => void): void {
  readline.emitKeypressEvents(process.stdin);
  process.stdin.setRawMode(true);
  process.stdin.on('keypress', (str, key) => {
    if (key.ctrl && key.name === 'c') {
      process.exit();
    } else if (['up', 'down', 'left', 'right'].includes(key.name)) {
      onDirectionChange(key.name as Direction);
    }
  });
}
```

Now, let's put it all together in `src/index.ts`:

```typescript
import { createInitialGameState, updateGameState } from './game';
import { renderGame } from './render';
import { setupInput } from './input';
import { GameState } from './types';

let state: GameState = createInitialGameState(20);

function gameLoop() {
  console.clear();
  console.log(renderGame(state));
  try {
    state = updateGameState(state);
    setTimeout(gameLoop, 200);
  } catch (e) {
    console.log('Game Over!');
    process.exit();
  }
}

setupInput((direction) => {
  state.direction = direction;
});

console.log('Use arrow keys to control the snake. Press Ctrl+C to exit.');

gameLoop();
```

## The Final Battle

Now, let's face our Final Boss! Compile and run the game:

```typescript
npx tsc && node dist/index.js
```

Use the arrow keys to control your snake. Watch it grow as it eats the food (●) and try not to collide with the walls or yourself!

## Congratulations, TypeScript Adventurer!

You've done it! You've conquered the Final Boss and created a fully functional Snake game using TypeScript. This project showcases your mastery of:

* TypeScript's type system
    
* Modular code structure
    
* Game loop implementation
    
* User input handling
    
* State management
    

As you guide your snake through the terminal battlefield, remember how far you've come in your TypeScript journey. From basic types to advanced features, you've grown into a true TypeScript Adventurer.

## What's Next? New Adventures Await!

But wait, there's more! Your TypeScript skills have prepared you for even greater adventures. On the horizon, we see the distant lands of AWS, where new challenges await:

* Serverless Framework: Use your TypeScript prowess to create scalable, serverless applications and infrastructure as code all in one project with MonoRepos.
    
* Terraform CDK: Craft infrastructure as code, defining and provisioning AWS resources with the power of TypeScript.
    

These new frontiers will test your skills in new ways, combining your TypeScript knowledge with cloud technologies to build even more powerful and scalable applications.

Are you ready for the next chapter of your coding adventure? The world of cloud computing awaits, and with your TypeScript skills, you're more than prepared to take it on!

Until then, keep coding, keep learning, and may your types always be strong and safe! 🚀🐍

# Thank You For Joining Me on This Adventure!
