TypeScript에 설치된 Robots를 팩스로 전송했습니다(CUI ver. 없음).
개시하다
지난 기사에서 이뤄진 로보츠.의 타입 스크립트 코드가 너무... 그래서 팩스로 보내봤어요.
분류도
의식적으로 설계MVC 모드.

Robot 클래스(Model)
지난번에는 하나였다.ts 파일로 모두 기술했지만 분업 후 여러 파일로 로보츠 게임을 구성했다.
// ロボットのタイプ(プレイヤー、敵、スクラップ)
export enum RobotType {
Player,
Enemy,
Scrap
}
// ロボットが動く方向
export enum RobotMove {
Teleport,
Wait,
Left,
Right,
Down,
Up,
LowerLeft,
LowerRight,
UpperLeft,
UpperRight,
Unknown
}
// 1体のロボットに関する情報 (座標とロボとのタイプ)
export type RobotInfo = {
x: number,
y: number,
type: RobotType
}
// ロボットの操作に必要な関数をinterfaceで予め定義
export interface RobotRules {
makeRobots(width: number, height: number, level: number): void;
canPutRobot(x: number, y: number): boolean;
canMove(x: number, y: number, width: number, height: number): boolean;
movePlayer(toward: RobotMove, width: number, height: number): boolean;
moveEnemey(): boolean;
wipeOut(): boolean;
countDeadEnemy(): number;
countTotalDeadEnemy(level: number): number;
}
// ロボットの管理
export class Robot implements RobotRules {
robotList: RobotInfo[];
constructor() {
this.robotList = [];
}
// ロボットの初期配置(プレイヤー、敵同時に行う)
makeRobots(width: number, height: number, level: number): void {
this.robotList = [];
// 0番目は Player
let x = Math.floor((Math.random() * width) + 1);
let y = Math.floor((Math.random() * height) + 1);
this.robotList.push({ x, y, type: RobotType.Player });
// 1番目から (numOfRobots-1)番目はEnemy
let count = 0;
const numOfEnemy = level * 10;
while (count < numOfEnemy) {
x = Math.floor((Math.random() * width) + 1);
y = Math.floor((Math.random() * height) + 1);
if (!this.canPutRobot(x, y)) {
// 同じ場所にロボットを置かない
continue;
}
this.robotList.push({ x, y, type: RobotType.Enemy });
count++;
}
}
// ロボットの配置ができるかチェック
canPutRobot(x: number, y: number): boolean {
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < this.robotList.length; i++) {
if (x === this.robotList[i].x && y === this.robotList[i].y) {
return false;
}
}
return true;
}
// プレイヤーロボットが正しく動けるかチェック
canMove(x: number, y: number, width: number, height: number): boolean {
// フィールド外に出ないかチェック
if (x === 0 || y === 0 || x === width + 1 || y === height + 1)
return false;
// 移動先にスクラップがあるかチェック
for (let i = 1; i < this.robotList.length; i++) {
if (this.robotList[i].x === x && this.robotList[i].y === y && this.robotList[i].type === RobotType.Scrap) {
return false;
}
}
return true;
}
// プレイヤーロボットの移動
movePlayer(toward: RobotMove, width: number, height: number): boolean {
let x = this.robotList[0].x;
let y = this.robotList[0].y;
switch (toward) {
case RobotMove.Wait:
break;
case RobotMove.Teleport:
do {
x = Math.floor((Math.random() * width) + 1);
y = Math.floor((Math.random() * height) + 1);
} while (!this.canMove(x, y, width, height));
break;
case RobotMove.Up:
y--;
break;
case RobotMove.Down:
y++;
break;
case RobotMove.Left:
x--;
break;
case RobotMove.Right:
x++;
break;
case RobotMove.UpperLeft:
x--;
y--;
break;
case RobotMove.UpperRight:
x++;
y--;
break;
case RobotMove.LowerLeft:
x--;
y++;
break;
case RobotMove.LowerRight:
x++;
y++;
break;
case RobotMove.Unknown:
return false;
}
if (!this.canMove(x, y, width, height)) {
return false;
}
this.robotList[0].x = x;
this.robotList[0].y = y;
return true;
}
// プレイヤーを動かした後に敵を一マス動かす
moveEnemey(): boolean {
for (const item of this.robotList) {
if (item.type === RobotType.Player || item.type === RobotType.Scrap) {
continue;
}
// プレイヤーの位置に向かうように敵を一マス動かす
if (this.robotList[0].x === item.x && this.robotList[0].y > item.y) {
item.y++;
} else if (this.robotList[0].x === item.x && this.robotList[0].y < item.y) {
item.y--;
} else if (this.robotList[0].x > item.x && this.robotList[0].y === item.y) {
item.x++;
} else if (this.robotList[0].x < item.x && this.robotList[0].y === item.y) {
item.x--;
} else if (this.robotList[0].x < item.x && this.robotList[0].y < item.y) {
item.x--;
item.y--;
} else if (this.robotList[0].x < item.x && this.robotList[0].y > item.y) {
item.x--;
item.y++;
} else if (this.robotList[0].x > item.x && this.robotList[0].y < item.y) {
item.x++;
item.y--;
} else if (this.robotList[0].x > item.x && this.robotList[0].y > item.y) {
item.x++;
item.y++;
}
}
// 敵同士が衝突したらスクラップにする
const length = this.robotList.length
for (let i = 1; i < length - 1; i++) {
for (let j = i + 1; j < length; j++) {
if ((this.robotList[i].x === this.robotList[j].x) && (this.robotList[i].y === this.robotList[j].y)) {
this.robotList[i].type = RobotType.Scrap;
this.robotList[j].type = RobotType.Scrap;
}
}
}
// プレイヤーと敵が衝突したらゲームオーバー
for (let i = 1; i < length; i++) {
if ((this.robotList[0].x === this.robotList[i].x && this.robotList[0].y === this.robotList[i].y)) {
return false;
}
}
return true;
}
// 全滅チェック
wipeOut(): boolean {
for (let i = 1; i < this.robotList.length; i++) {
if (this.robotList[i].type === RobotType.Enemy) {
return false;
}
}
return true;
}
// 倒した敵の数
countDeadEnemy(): number {
const length = this.robotList.length;
let count = 0;
for (let i = 1; i < length; i++) {
if (this.robotList[i].type === RobotType.Scrap) {
count++;
}
}
return count;
}
// 累計で倒した敵の数
countTotalDeadEnemy(level: number): number {
let total = 0;
for (let l = level - 1; l > 0; l--) {
total += l * 10;
}
return total + this.countDeadEnemy();
}
}
Field 클래스(View)
import { RobotInfo, RobotType } from "./robot"
// フィールドサイズをreadonlyで宣言
export interface FieldSize {
readonly width: number,
readonly height: number,
}
// フィールド表示に必要な関数
export interface FieldMethod {
printField(): void;
printGuide(level: number, score: number): void;
printRobots(robotList: RobotInfo[]): void;
}
// 上記インタフェースを実装したFieldクラス
export class FieldCUI implements FieldSize, FieldMethod {
readonly width = 60;
readonly height = 20;
// tslint:disable-next-line: no-empty
constructor() {
}
printField(): void {
// tslint:disable-next-line: no-console
console.clear()
// top of field
process.stdout.write("+")
for (let i = 0; i < this.width; i++) {
process.stdout.write("-")
}
process.stdout.write("+\n")
// inside of field
for (let j = 0; j < this.height; j++) {
process.stdout.write("|")
for (let i = 0; i < this.width; i++) {
process.stdout.write(" ")
}
process.stdout.write("|\n")
}
// bottom of field
process.stdout.write("+")
for (let i = 0; i < this.width; i++) {
process.stdout.write("-")
}
process.stdout.write("+")
}
printGuide(level: number, score: number): void {
// tslint:disable-next-line: variable-name
const cursor_x = this.width + 3
// tslint:disable-next-line: variable-name
let cursor_y = 0
process.stdout.cursorTo(cursor_x, cursor_y)
process.stdout.write("\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
cursor_y++
process.stdout.write("Directions:\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("y k u\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write(" \\|/\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("h- -l\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write(" /|\\ \n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("b j n\n\n")
cursor_y++
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("Commands:\n\n")
cursor_y++
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("w: wait for end\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("t: teleport\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("q: quit\n\n")
cursor_y++
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("Legend:\n\n")
cursor_y++
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("+: robot\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("*: junk heap\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("@: you\n\n")
cursor_y++
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("Level:" + level + "\n\n")
process.stdout.cursorTo(cursor_x, cursor_y++)
process.stdout.write("Score:" + score + "\n\n")
}
printRobots(robotList: RobotInfo[]) {
for (const item of robotList) {
process.stdout.cursorTo(item.x, item.y)
if (item.type === RobotType.Player) {
// put player robot
process.stdout.write('@')
} else if (item.type === RobotType.Enemy) {
// put enemy robots
process.stdout.write('+')
} else if (item.type === RobotType.Scrap) {
// put scrap
process.stdout.write('*')
} else {
;
}
}
}
}
게임 클래스(Ctroller에 해당)
import { FieldCUI } from './field';
import { Robot, RobotMove } from './robot'
export class Game {
level: number;
score: number;
constructor() {
this.level = 1;
this.score = 0;
}
// Robots Start
start(): void {
const robot = new Robot();
const fieldCUI = new FieldCUI();
robot.makeRobots(fieldCUI.width, fieldCUI.height, this.level);
fieldCUI.printField();
fieldCUI.printRobots(robot.robotList);
fieldCUI.printGuide(this.level, this.score);
// keypressライブラリを読み込む
const keypress = require('keypress');
// keypressを標準入力に設定
keypress(process.stdin);
process.stdin.on('keypress', (ch: any, key: any) => {
let toward: RobotMove = RobotMove.Wait;
switch (ch) {
case 'y':
toward = RobotMove.UpperLeft;
break;
case 'k':
toward = RobotMove.Up;
break;
case 'u':
// 右上に1マス移動
toward = RobotMove.UpperRight;
break;
case 'l':
// 右に1マス移動
toward = RobotMove.Right;
break;
case 'n':
// 右下に1マス移動
toward = RobotMove.LowerRight;
break;
case 'j':
// 下に1マス移動
toward = RobotMove.Down;
break;
case 'b':
// 左下に1マス移動
toward = RobotMove.LowerLeft;
break;
case 'h':
// 左に1マス移動
toward = RobotMove.Left;
break;
case 'w':
// 待機
break;
case 't':
// スクラップ以外にテレポート. 運が悪いと敵の隣にテレポートで即死.
toward = RobotMove.Teleport;
break;
case 'q':
toward = RobotMove.Unknown;
process.stdin.pause();
break;
default:
toward = RobotMove.Wait;
}
if (robot.movePlayer(toward, fieldCUI.width, fieldCUI.height)) {
// ゲームオーバーのとき
if (!robot.moveEnemey()) {
process.stdout.write("Game Over...\n");
process.stdin.pause();
} else {
// 敵が全滅
if (robot.wipeOut()) {
robot.makeRobots(fieldCUI.width, fieldCUI.height, ++(this.level));
}
// ボーナス点を加味したスコア
let bonusSum = 0;
for (let level = this.level - 1; level > 0; level--) {
bonusSum += level * 100;
}
this.score = robot.countTotalDeadEnemy(this.level) * 10
+ bonusSum;
// 画面表示
fieldCUI.printField();
fieldCUI.printRobots(robot.robotList);
fieldCUI.printGuide(this.level, this.score);
}
}
});
// プロセス実行中のキー入力を拾うように設定
process.stdin.setRawMode(true);
process.stdin.resume();
}
}
Primary(엔트리 포인트와 같음)
import { Game } from './game'
const game = new Game();
game.start();
소스 코드
GitHub가 공개됐다.
동작 확인

끝말
Reference
이 문제에 관하여(TypeScript에 설치된 Robots를 팩스로 전송했습니다(CUI ver. 없음).), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/takanassyi/articles/d5dc835a4e27883b5a8c텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)