Skip to content

Commit

Permalink
finished
Browse files Browse the repository at this point in the history
  • Loading branch information
yanceyy committed Oct 2, 2021
1 parent c65469f commit 9f50e18
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 12 deletions.
4 changes: 1 addition & 3 deletions .eslintrc.js
Expand Up @@ -13,9 +13,7 @@ module.exports = {
ecmaVersion: 12,
sourceType: 'module',
},
plugins: [
'@typescript-eslint',
],
plugins: ['@typescript-eslint'],
rules: {
'no-console': 'warn',
'no-unused-vars': 0,
Expand Down
20 changes: 19 additions & 1 deletion README.md
@@ -1 +1,19 @@
# A snake game writted by TypeScript
# Description

This is a basic snake game writted by TypeScript

## Install dependencies

```bash
npm i
```

## Run

```bash
npm start
```

## License

[MIT](https://choosealicense.com/licenses/mit/)
25 changes: 25 additions & 0 deletions src/food.ts
@@ -0,0 +1,25 @@
export default class Food {
element:HTMLElement;

constructor() {
// get food element and store
this.element = document.getElementById('food')!;
}

// get position of the food
get X():number {
return this.element.offsetLeft;
}

get Y():number {
return this.element.offsetTop;
}

// change food position
changePosition():void {
const top = Math.round(Math.random() * 29) * 10;
const left = Math.round(Math.random() * 29) * 10;
this.element.style.left = `${top}px`;
this.element.style.top = `${left}px`;
}
}
69 changes: 67 additions & 2 deletions src/index.less
@@ -1,4 +1,69 @@
*{
padding: 0;
margin: 0;
box-sizing: border-box;
}

body{
transform: translateX(30px);
display: flex;
}
justify-content: center;
align-items: center;
height: 100vh;
.container{
width: 360px;
height: 420px;
background-color: #999;
border: 5px black solid;
border-radius:30px;
.screen{
width: 304px;
height: 304px;
border:2px black solid;
margin: 18px auto;
position: relative;
#snake{
&>div{
width: 10px;
height: 10px;
background-color: #000;
padding:1px;
background-clip: content-box;
position: absolute;
}
}
#food{
width: 10px;
height: 10px;
background-color: #000;
position: absolute;
left:20px;
top:20px;
border-radius:50%;
}
#restart{
font-size: 2rem;
background-color: transparent;
color: white;
border: none;
position: absolute;
top:50%;
left:50%;
transform: translate(-50%, -50%);
cursor: pointer;

}
}
.info{
font: 1rem;
font-weight: bolder;
padding:20px;
div:first-child{
float: left;
}
div:last-child{
float: right;
}
}
}
}

93 changes: 87 additions & 6 deletions src/index.ts
@@ -1,10 +1,91 @@
/* eslint-disable import/extensions */
/* eslint-disable import/no-unresolved */
import './index.less';
import Food from './food';
import Info from './info';
import Snake from './snake';

const nihd = 'hello';
console.log('nihao');
function sum(a:number):number {
return a + 4;
class Game {
snake:Snake;

food:Food;

info:Info;

direction:string='ArrowRight';

isLive = true;

startButton:HTMLElement;

keydownHandler = (e:KeyboardEvent) => {
if (e.key.startsWith('Arrow')) {
this.direction = e.key;
}
}

restart = () => {
this.snake = new Snake();
this.food.changePosition();
this.info = new Info();
this.startButton.style.display = 'none';
this.direction = 'ArrowRight';
this.start();
}

constructor() {
this.snake = new Snake();
this.food = new Food();
this.info = new Info();
this.startButton = document.getElementById('restart')!;
this.startButton.addEventListener('click', this.restart);
document.addEventListener('keydown', this.keydownHandler);
}

hasEat(x:number, y:number) {
return x === this.food.X && y === this.food.Y;
}

start() {
let { X, Y } = this.snake;

switch (this.direction) {
case 'ArrowUp': {
Y -= 10;
break;
}
case 'ArrowDown': {
Y += 10;
break;
}
case 'ArrowLeft': {
X -= 10;
break;
}
case 'ArrowRight': {
X += 10;
break;
}
default:
}

if (this.hasEat(X, Y)) { // if is ture then it is safe and no error
this.food.changePosition();
this.info.addScore();
this.snake.increaseLength(X, Y);
} else {
// may cause error since eat self or cash wall
try {
this.snake.eatSelf(X, Y);
this.snake.move(X, Y);
} catch (e) {
this.startButton.style.display = 'block';
return;
}
}
// eslint-disable-next-line no-unused-expressions
this.isLive && setTimeout(this.start.bind(this), (12 - this.info.level) * 50);
}
}

console.log(sum(4));
console.log(sum(4));
const game = new Game();
34 changes: 34 additions & 0 deletions src/info.ts
@@ -0,0 +1,34 @@
export default class Info {
score=0;

level=1;

scoreEle:HTMLElement;

levelEle:HTMLElement;

maxLEVEL:number;

upScore:number;

constructor(maxLEVEL:number = 10, upScore:number = 3) {
this.scoreEle = document.getElementById('score')!;
this.levelEle = document.getElementById('level')!;
this.maxLEVEL = maxLEVEL;
this.upScore = upScore;
}

// add score
addScore():void {
this.score += 1;
this.scoreEle.textContent = String(this.score);
if (this.score % this.upScore === 0) this.levelUp();
}

levelUp():void {
if (this.level < 10) {
this.level += 1;
this.levelEle.textContent = String(this.level);
}
}
}
61 changes: 61 additions & 0 deletions src/snake.ts
@@ -0,0 +1,61 @@
export default class Snake {
head:HTMLElement;

bodies:HTMLCollection;

snake:HTMLElement;

constructor() {
this.snake = document.getElementById('snake')!;
this.init();
this.head = document.querySelector('#snake > div')!;
this.bodies = this.snake.getElementsByTagName('div')!;
}

init() {
this.snake.innerHTML = '<div></div>'; // restart and make sure only one div exists
}

// get the head of the Snake
get X() {
return this.head.offsetLeft;
}

get Y() {
return this.head.offsetTop;
}

// set the head position of the snake
setPosition(x: number, y: number) {
if (x < 0 || x > 290 || y < 0 || y > 290) throw new Error('dead');
this.head.style.left = `${x}px`;
this.head.style.top = `${y}px`;
}

// test whether next move will eat self
eatSelf(x:number, y:number) {
if ([...this.bodies].some((body) => body !== this.head
&& x === (<HTMLScriptElement>body).offsetLeft
&& y === (<HTMLScriptElement>body).offsetTop)) { throw new Error('dead'); }
}

// increase length at the next setp position
increaseLength(x: number, y: number) {
const step = document.createElement('div');
this.snake.insertBefore(step, this.head);
this.head = step;
this.setPosition(x, y);
}

// move
// remove the last child add to the front of the child
move(x: number, y: number) {
if (x < 0 || x > 290 || y < 0 || y > 290) throw new Error('dead');
const lastChild:HTMLElement = this.snake.querySelector('div:last-child')!;
const step = document.createElement('div');
this.snake.insertBefore(step, this.head);
this.head = step;
this.snake.removeChild(lastChild);
this.setPosition(x, y); // use set position to add new div to right position
}
}
13 changes: 13 additions & 0 deletions src/template/index.html
Expand Up @@ -9,6 +9,19 @@
</head>

<body>
<div class="container">
<div class="screen">
<div id="snake">
<div></div>
</div>
<button id='restart'> Start </button>
<div id="food"></div>
</div>
<div class="info">
<div>SCORE: <span id="score">0</span></div>
<div>LEVEL: <span id="level">1</span></div>
</div>
</div>
</body>

</html>
4 changes: 4 additions & 0 deletions webpack.config.js
Expand Up @@ -8,6 +8,10 @@ module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
environment: {
arrowFunction: false,
const: false,
},
},
module: {
rules: [
Expand Down

1 comment on commit 9f50e18

@vercel
Copy link

@vercel vercel bot commented on 9f50e18 Feb 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.