2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Secundum consilium priorem, elementa ludi necessarii opus est ut ludus committitur. Exemplum Tetris in console explicandi cursus Sit.
import { ConsoleCanvas, ConsoleController, ConsoleColorTheme, Color } from '@shushanfx/tetris-console';
import { Dimension, ColorFactory, Game } from '@shushanfx/tetris-core';
const theme = new ConsoleColorTheme();
const canvas = new ConsoleCanvas(theme);
const controller = new ConsoleController();
const dimension = new Dimension(10, 20);
const factory = new ColorFactory(dimension, [
Color.red,
Color.green,
Color.yellow,
Color.blue,
Color.magenta,
Color.cyan,
]);
const game = new Game({ dimension, canvas, factory, controller });
game.start();
Analyze eam lineam in linea:
Introductio fasciculorum dividitur in nucleum tetris et consolatorium. Haec est divisio fasciculorum.
Initializationis thematis, canvas et moderatoris;
Initialization officinarum et dimensionum;
Ad lusum initializationem, uti carbasa initialized antea, officinas, canvas, et objecta dimensio;
Ludum vocat modum initium.
Deinde inspice quid facit initium?
class Game {
start() {
const { status } = this;
if (status === GameStatus.RUNNING) {
return ;
}
if (status === GameStatus.OVER
|| status === GameStatus.STOP) {
this.stage.reset();
this.canvas.render();
} else if (status === GameStatus.READY) {
this.controller?.bind();
this.canvas.render();
}
this.status = GameStatus.RUNNING;
this.tickCount = 0;
this.canvas.update();
// @ts-ignore
this.tickTimer = setInterval(() => {
if (this.tickCount == 0) {
// 处理向下
this.stage.tick();
this.checkIsOver();
}
this.canvas.update();
this.tickCount++;
if (this.tickCount >= this.tickMaxCount) {
this.tickCount = 0;
}
}, this.speed);
}
}
Expande eam lineam per lineam:
obtinestatus
variabilis;status
for*Game
Interna repraesentatio ludi status, respective准备就绪(READY)
、游戏中(RUNNING)
、暂停(PAUSE)
、停止(STOP)
、游戏结束(OVER)
. Discrimen finis inter finem et ludum est quod ille sistit activum ludum, hic vero finem logicae ludi facit et finem facit ludum.
Si ludus in cursu est, directe redi;
Si ludus sistitur et ludus est, tuncStage
praestare et resetcanvas
Praestare altiore redraw.
Si ludus perseveret dum parat, ludus significat initializationem modo completam et numquam incepit. Gubernatorem voca ad res ligandas et primum carbasa trahere;
Constitue ludo civitatis to 游戏中(RUNNING)
, status internus tickCount = 0;
translatiocanvas
Praecipua renovatio partialis illico praestanda est. Hic principalis renovatio est quod status mutatus est, ut status ludi causando opus restituatur;
In mauris metus, tempus faucibus cursus.speed
In futurum, eam congruentem cum gradu ludi (non sustinetur adhuc).
Causa quare tickCount mechanismum introducitur, maxime est ad frequentiam carbasi curandam.
Core logica ludi, ut ex superiori codice videri poteststage.tick
interna exsecutio talis est;
class Stage {
tick(): void {
if (this.isOver || this.clearTimers.length > 0) {
return;
}
// 首次加载,current为空
if (!this.current) {
this.next = this.factory.randomBlock();
this.toTop(this.next);
this.current = this.factory.randomBlock();
this.toTop(this.current);
return ;
}
const isOver = this.current.points.some((point) => {
return !this.points[point.y][point.x].isEmpty;
});
if (isOver) {
this.isOver = true;
return;
}
const canMove = this.current.canMove('down', this.points);
if (canMove) {
this.current.move('down');
} else {
this.handleClear();
}
}
}
Primum determinare utrum ludus praesit vel operatio tersus exercetur.
sicurrent
Si inanis, ludus est oneratus primum et separatim initialized.current
etnext
。
Utrum ludus conditionis finem attingat, id estcurrent
etpoints
Aliudque est. Si aliudque est, ludus ut supra designatus est.
Decernite an hic currens possit deorsum movere.
Deinde videamus quomodo eliminationem deprehendere, id esthandleClear
effectio.
class Stage {
private handleClear() {
if (!this.current) {
return;
}
// 1. 复制新的points
const pointsClone: Point[][] = this.points.map((row) => row.map((point) => point.clone()));
this.current.points.forEach((point) => {
pointsClone[point.y][point.x] = point.clone();
});
// 2. 检查是否有消除的行
const cleanRows: number[] = [];
for(let i = 0; i < pointsClone.length; i ++) {
const row = pointsClone[i];
const isFull = row.every((point) => {
return !point.isEmpty
});
if (isFull) {
cleanRows.push(i);
}
}
// 3. 对行进行消除
if (cleanRows.length > 0) {
this.startClear(pointsClone, cleanRows, () => {
// 处理计算分数
this.score += this.getScore(cleanRows.length);
// 处理消除和下落
cleanRows.forEach((rowIndex) => {
for(let i = rowIndex; i >= 0; i--) {
if (i === 0) {
pointsClone[0] = Array.from({ length: this.dimension.xSize }, () => new Point(-1, -1));
} else {
pointsClone[i] = pointsClone[i - 1];
}
}
});
// 4. 扫尾工作,变量赋值
this.points = pointsClone;
this.current = this.next;
this.next = this.factory.randomBlock();
this.toTop(this.next);
});
} else {
// 4. 扫尾工作,变量赋值
this.points = pointsClone;
this.current = this.next;
this.next = this.factory.randomBlock();
this.toTop(this.next);
}
}
}
Totus processus in quatuor gradus, sicut ex praedicto codice videri potest, dividitur.
Effingo nova puncta Clone, inclusa currenti et currenti puncta.
Deprehende punctaClone lineam per lineam, et nota, si tota linea impleatur;
Linea dele per lineam secundum contentum notatum in II generatum. Nota quod operatio deletionis a summo ad imum perficitur.
Purgato opere.Hic gradus opus est ad faciendam sive defensionem operationis faciendamthis.points
Perfice simulcurrent
etnext
transibit.
Quid agitur cum rotatione cuneos?
Omnes gyrationis mores in ludo.rotate methodo vocando utitur, etiam eventus a moderatore definito, vocato externo, etc.;
Logica in Ludo effecta haec est:
class Game {
rotate() {
this.stage.rotate();
this.canvas.update();
}
}
Vigilate alteraStage
Effectio
class Stage {
rotate(): boolean {
if (!this.current) {
return false;
}
const canChange = this.current.canRotate(this.points);
if (canChange) {
this.current.rotate();
}
return false;
}
}
judicas primumcurrent
Sive sit, si non est, directe reddetur;
translatiocurrent
of*canRotate
Modus inspiciendi utrum positio hodierna revolvi possit;
Eamus longius et videBlock
of*canRotate
etrotate
modum.
class Block {
canRotate(points: Point[][]): boolean {
const centerIndex = this.getCenterIndex();
if (centerIndex === -1) {
return false;
}
const changes = this.getChanges();
if (changes.length === 0) {
return false;
}
const nextChange = changes[(this.currentChangeIndex + 1) % changes.length];
const newPoints = this.changePoints(this.points, this.points[centerIndex], nextChange);
const isValid = Block.isValid(newPoints, this.dimension);
if (isValid) {
return newPoints.every((point) => {
return points[point.y][point.x].isEmpty;
});
}
return isValid;
}
}
Inspice primumcanRotate
effectio.
Get centerIndex, centerIndex est index centri punctum rotationis. Singulae graphicae diversae sunt, ut IBlock, quae sic definitur:
class IBlock extends Block {
getCenterIndex(): number {
return 1;
}
}
Id est, centrum gyrationis punctum est secundus nodi.sicut口口口口
, punctum centrum secundi口田口口
。
Praeterea cum hunc scandalum cogitamus, etiam duxi impedimenta aliqua rotari non posse, ut OBlock, quod seligi non potest.sedgetCenterIndex
reditus-1
。
Ordinationes mutationes obtine. Ordinatio definitur angulus rotationis currentis. Longitudo ordinata numerus rotarum repraesentat.sicutIBlock
definitur sic:
class IBlock extends Block {
currentChangeIndex: number = -1;
getChanges(): number[] {
return [
Math.PI / 2,
0 - Math.PI / 2
];
}
}
Id est, prima rotatio est status initialis Math.PI/2 (id est 90 gradus), et secunda rotatio -Math.PI/2 primae rotationis (id est -90 gradus). sic:
// 初始状态
// 口田口口
// 第一次旋转
// 口
// 田
// 口
// 口
// 第二次旋转
// 口田口口
PS: Quaeso hic nota quod axis coordinatus est a sinistro ad dextram et ad imum.
Iudicium enim rotationis, re- gulae iudicium sunt;
Videmus ergo quod suntisValid
etnewPoints.every
judicium.
Deinde videamusBlock.rotate
sic:
class Block {
rotate() {
const centerIndex = this.getCenterIndex();
if (centerIndex === -1) {
return false;
}
const changes = this.getChanges();
if (changes.length === 0) {
return false;
}
const nextChange = changes[(this.currentChangeIndex + 1) % changes.length];
const newPoints = this.changePoints(this.points, this.points[centerIndex], nextChange);
const isValid = Block.isValid(newPoints, this.dimension);
if (isValid) {
this.currentChangeIndex = (this.currentChangeIndex + 1) % changes.length;
this.points = newPoints;
}
return isValid;
}
}
Per descriptionem, etc.rotate
Ratiocinatio facilis est ad intelligendum.
obtinecenterIndex
etchanges
, VoluntascurrentChangeIndex
Incrementum cyclicum praestare et impedimentum coordinatis novis monstrare.
incurrentChangeIndex
Valor initialis est -1, quod significat rotationem currentem in progressu, et si maior quam vel 0 aequalis est, significat index + 1 selegisse. (Quaeso hic diligenter cogita, quia index ordinatae ab 0 incipit)
Motus significat movere Clausum in quattuor partes.Videamus eius exsecutionem
class Game {
move(direction: Direction) {
this.stage.move(direction);
this.canvas.update();
}
}
Inter quos sic definitur Ditio:
type Direction = 'up' | 'down' | 'left' | 'right';
Vide ampliusStage
Exsecutio:
class Stage {
move(direction: Direction) {
if (!this.current) {
return false;
}
const canMove = this.current.canMove(direction, this.points);
if (canMove) {
this.current.move(direction);
}
return canMove;
}
}
Vide ampliuscanMove
etmove
effectio.
class Block {
canMove(direction: Direction, points: Point[][]): boolean {
return this.points.every((point) => {
switch (direction) {
case 'up':
return point.y > 0 && points[point.y - 1][point.x].isEmpty;
case 'down':
return point.y < this.dimension.ySize - 1 && points[point.y + 1][point.x].isEmpty;
case 'left':
return point.x > 0 && points[point.y][point.x - 1].isEmpty;
case 'right':
return point.x < this.dimension.xSize - 1 && points[point.y][point.x + 1].isEmpty;
}
});
};
}
Breviter hoc modo vertamus:
Ad sursum movendum omnia puncta y-axa majora esse debent quam 0 (id est, majora vel aequalia 1), et puncta post motum vacua esse debent puncta;
Sinistra diverticula, puncta omnia x-axa majora esse debent quam 0 (id est, majora vel aequalia 1), punctaque post motum vacua puncta esse debent;
Trabea ad dextram, omnia puncta x-axis minora esse debent quam longitudo axis x-coordinate -1 (hoc est, minor quam vel aequalis xSize - 2), et puncta post motum debent esse puncta vacua;
Ad movendum omnia puncta y-axis minora esse debent quam longitudo axis -1 (hoc est, minor quam vel aequalis ySize - 2), et puncta post motum debent esse puncta vacua.
Postquam condiciones motus occurrentes, inspiciamusmove
effectio.
class Block {
move(direction: Direction): boolean {
switch (direction) {
case 'up':
this.points.forEach((point) => { point.y = point.y - 1})
break;
case 'down':
this.points.forEach((point) => { point.y = point.y + 1})
break;
case 'left':
this.points.forEach((point) => { point.x = point.x - 1})
break;
case 'right':
this.points.forEach((point) => { point.x = point.x + 1})
break;
}
return true;
}
}
Directe modificare valorem puncti coordinati.
In hoc capite tres magni mores ludi describit: defensionem, gyrationem, et motum. Tres eorum ad ludum complendum inter se cooperantur. In capite sequenti erimus instrumenti reddendi et operandi potestatem ludi.