From dc9149da302acfe57dcc8e801ce3a0adcb887ab0 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Thu, 17 Oct 2024 13:24:00 +0300 Subject: [PATCH 01/27] tests fix --- rpgsaga/saga/tests/person.spec.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rpgsaga/saga/tests/person.spec.ts b/rpgsaga/saga/tests/person.spec.ts index 3b9e946c5..448f42e03 100644 --- a/rpgsaga/saga/tests/person.spec.ts +++ b/rpgsaga/saga/tests/person.spec.ts @@ -3,27 +3,27 @@ import { Person } from '../src/person'; describe('Person class methods tests', () => { it('Constructor test', () => { let newPerson = new Person('TestPerson', 20, 'Male'); - expect(newPerson.age == 20); - expect(newPerson.name == 'TestPerson'); - expect(newPerson.sex == 'Male'); + expect(newPerson.age).toEqual(20); + expect(newPerson.name).toBe('TestPerson'); + expect(newPerson.sex).toBe('male'); }); describe('Get methods tests', () => { let newPerson = new Person('TestPerson', 20, 'Male'); it('Age get test', () => { - expect(newPerson.age == 20); + expect(newPerson.age).toEqual(20); }); it('Name get test', () => { - expect(newPerson.name == 'TestPerson'); + expect(newPerson.name).toBe('TestPerson'); }); it('Sex get test', () => { - expect(newPerson.sex == 'Male'); + expect(newPerson.sex).toBe('male'); }); }); describe('Set methods tests', () => { let newPerson = new Person('TestPerson', 20, 'Male'); it('Age basic test', () => { newPerson.age = 35; - expect(newPerson.age == 35); + expect(newPerson.age).toEqual(35); }); it('Age negative test', () => { expect(() => { @@ -32,11 +32,11 @@ describe('Person class methods tests', () => { }); it('Name test', () => { newPerson.name = 'John'; - expect(newPerson.name == 'John'); + expect(newPerson.name).toBe('John'); }); it('Sex basic test', () => { newPerson.sex = 'Female'; - expect(newPerson.sex == 'female'); + expect(newPerson.sex).toBe('female'); }); it('Sex uncorrect test', () => { expect(() => { @@ -47,13 +47,13 @@ describe('Person class methods tests', () => { describe('Other methods tests', () => { let newPerson = new Person('John', 20, 'Male'); it('Should return name and sex', () => { - expect(newPerson.toString() == `${newPerson.name} ${newPerson.sex}`); + expect(newPerson.toString()).toEqual(`${newPerson.name} ${newPerson.sex}`); }); it('Should return age as number', () => { - expect(newPerson.toNumber() == newPerson.age); + expect(newPerson.toNumber()).toEqual(newPerson.age); }); it('Should return all properties', () => { - expect(newPerson.print() == `first name: ${newPerson.name}\nsex: ${newPerson.sex}\nage: ${newPerson.age}`); + expect(newPerson.print()).toEqual(`first name: ${newPerson.name}\nsex: ${newPerson.sex}\nage: ${newPerson.age}`); }); }); }); From fe3a4212ec3bd612160f1a3f530fe8896a5a8d63 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Mon, 21 Oct 2024 18:37:20 +0300 Subject: [PATCH 02/27] rpg saga --- rpgsaga/saga/src/function.ts | 51 +++++++++++++++++++++++++++ rpgsaga/saga/src/index.ts | 56 ++++++------------------------ rpgsaga/saga/tests/example.spec.ts | 2 +- 3 files changed, 63 insertions(+), 46 deletions(-) create mode 100644 rpgsaga/saga/src/function.ts diff --git a/rpgsaga/saga/src/function.ts b/rpgsaga/saga/src/function.ts new file mode 100644 index 000000000..02588ecdf --- /dev/null +++ b/rpgsaga/saga/src/function.ts @@ -0,0 +1,51 @@ +function result(a: number, x: number): number { + return Math.pow(a, Math.pow(x, 2) - 1) - Math.log10(Math.pow(x, 2) - 1) + Math.cbrt(Math.pow(x, 2) - 1); +} + +function taskA(a: number, xBegin: number, xEnd: number, xDelta: number): number[] { + const y: number[] = []; + if (xDelta === 0) { + return []; + } + if (xDelta > 0) { + for (let x = xBegin; x <= xEnd; x += xDelta) { + y.push(result(a, x)); + } + } else { + for (let x = xBegin; x >= xEnd; x += xDelta) { + if (Math.pow(x, 2) - 1 <= 0) { + continue; + } + y.push(result(a, x)); + } + } + return y; +} + +function taskB(a: number, values: number[]): number[] { + if (values.length === 0) { + return []; + } + const y: number[] = []; + for (const x of values) { + y.push(result(a, x)); + } + return y; +} + +function output(nameOfTask: string, results: number[]): string { + let resMsg = `Solutions to task ${nameOfTask}:\n`; + + results.forEach(resultNum => { + resMsg += `${resultNum},\n`; + }); + return resMsg; +} + +const taskAResult = taskA(1.6, 2, 10, 0); +console.log(output('A', taskAResult)); + +const taskBResult = taskB(1.6, []); +console.log(output('B', taskBResult)); + +export { taskA, taskB }; diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index 02588ecdf..434f622d6 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,51 +1,17 @@ -function result(a: number, x: number): number { - return Math.pow(a, Math.pow(x, 2) - 1) - Math.log10(Math.pow(x, 2) - 1) + Math.cbrt(Math.pow(x, 2) - 1); -} - -function taskA(a: number, xBegin: number, xEnd: number, xDelta: number): number[] { - const y: number[] = []; - if (xDelta === 0) { - return []; - } - if (xDelta > 0) { - for (let x = xBegin; x <= xEnd; x += xDelta) { - y.push(result(a, x)); - } - } else { - for (let x = xBegin; x >= xEnd; x += xDelta) { - if (Math.pow(x, 2) - 1 <= 0) { - continue; - } - y.push(result(a, x)); - } - } - return y; -} +class Gamer { + protected _health: number; + protected _strength: number; + protected _name: string; -function taskB(a: number, values: number[]): number[] { - if (values.length === 0) { - return []; + constructor(gamerHealth, gamerStrength, gamerName) { + this._health = gamerHealth; + this._strength = gamerStrength; + this._name = gamerName; } - const y: number[] = []; - for (const x of values) { - y.push(result(a, x)); - } - return y; -} - -function output(nameOfTask: string, results: number[]): string { - let resMsg = `Solutions to task ${nameOfTask}:\n`; - - results.forEach(resultNum => { - resMsg += `${resultNum},\n`; - }); - return resMsg; } -const taskAResult = taskA(1.6, 2, 10, 0); -console.log(output('A', taskAResult)); +class Knight extends Gamer {} -const taskBResult = taskB(1.6, []); -console.log(output('B', taskBResult)); +class Vizard extends Gamer {} -export { taskA, taskB }; +class Archer extends Gamer {} diff --git a/rpgsaga/saga/tests/example.spec.ts b/rpgsaga/saga/tests/example.spec.ts index acbec8ce1..e3eacea0f 100644 --- a/rpgsaga/saga/tests/example.spec.ts +++ b/rpgsaga/saga/tests/example.spec.ts @@ -1,4 +1,4 @@ -import { taskA, taskB } from '../src'; +import { taskA, taskB } from '../src/function'; describe('Tests taskA', () => { it('should return 6 values', () => { From c2aae0cc450db9929dae50ccc0d344ea000780fa Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Mon, 21 Oct 2024 18:40:41 +0300 Subject: [PATCH 03/27] rpg saga --- .../saga/src/{ => funcAndClass}/function.ts | 0 rpgsaga/saga/src/{ => funcAndClass}/person.ts | 0 rpgsaga/saga/src/index.ts | 242 +++++++++++++++++- rpgsaga/saga/tests/example.spec.ts | 2 +- rpgsaga/saga/tests/person.spec.ts | 2 +- 5 files changed, 233 insertions(+), 13 deletions(-) rename rpgsaga/saga/src/{ => funcAndClass}/function.ts (100%) rename rpgsaga/saga/src/{ => funcAndClass}/person.ts (100%) diff --git a/rpgsaga/saga/src/function.ts b/rpgsaga/saga/src/funcAndClass/function.ts similarity index 100% rename from rpgsaga/saga/src/function.ts rename to rpgsaga/saga/src/funcAndClass/function.ts diff --git a/rpgsaga/saga/src/person.ts b/rpgsaga/saga/src/funcAndClass/person.ts similarity index 100% rename from rpgsaga/saga/src/person.ts rename to rpgsaga/saga/src/funcAndClass/person.ts diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index 434f622d6..346777f3f 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,17 +1,237 @@ -class Gamer { - protected _health: number; - protected _strength: number; - protected _name: string; +class Logger { + private logFile: string; - constructor(gamerHealth, gamerStrength, gamerName) { - this._health = gamerHealth; - this._strength = gamerStrength; - this._name = gamerName; + constructor(logFile: string = 'game_log.txt') { + this.logFile = logFile; + } + + static log(message: string): void { + const timestamp = new Date().toISOString(); + const logEntry = `${timestamp}: ${message}\n`; + console.log(logEntry); + } +} + +abstract class Player { + protected health: number; + protected strength: number; + protected name: string; + isAlive: boolean = true; + + constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { + this.health = gamerHealth; + this.strength = gamerStrength; + this.name = gamerName; + } + + public getHealth(): number { + return this.health; + } + + public getStrength(): number { + return this.strength; + } + + public getName(): string { + return this.name; + } + + public abstract attack(opponent: Player): void; + + public takeDamage(damage: number): void { + this.health -= damage; + if (this.health <= 0) { + this.isAlive = false; + Logger.log(`(${this.constructor.name}) ${this.name} погибает`); + } + } + + public allowToAttack(opponent: Player): boolean { + return this.isAlive; + } +} + +class Knight extends Player { + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + this.skillUsed = true; + const skillDamage = this.strength * 1.3; + Logger.log( + `(${this.constructor.name}) ${ + this.name + } использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + opponent.takeDamage(skillDamage); + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack(opponent)) { + const damage = this.strength; + Logger.log( + `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } } } -class Knight extends Gamer {} +class Vizard extends Player { + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + Logger.log( + `(${this.constructor.name}) ${this.name} использует (Заворожение) на (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + this.skillUsed = true; + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack(opponent)) { + const damage = this.strength; + Logger.log( + `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } + } + + public takeDamage(damage: number): void { + if (this.skillUsed) { + Logger.log(`Противник не может атаковать ${this.name} из-за (Заворожения)`); + this.skillUsed = false; + } else { + super.takeDamage(damage); + } + } +} -class Vizard extends Gamer {} +class Archer extends Player { + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + Logger.log( + `(${this.constructor.name}) ${this.name} использует (Огненные стрелы) на (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + this.skillUsed = true; + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack(opponent)) { + let damage = this.strength; + if (this.skillUsed) { + damage += 2; + } + Logger.log( + `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ + opponent.constructor.name + }) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } + } +} + +class Game { + private players: Player[]; + + constructor(playerCount: number) { + this.players = []; + const names = ['Эльдар', 'Артур', 'Гэндальф', 'Вильямс']; + + for (let i = 0; i < playerCount; i++) { + const name = names[Math.floor(Math.random() * names.length)]; + const health = Math.floor(Math.random() * 50) + 50; + const strength = Math.floor(Math.random() * 10) + 10; + this.players.push(this.createPlayer(name, health, strength)); + } + } + + private createPlayer(name: string, health: number, strength: number): Player { + const types = [Knight, Archer, Vizard]; + const PlayerClass = types[Math.floor(Math.random() * types.length)]; + return new PlayerClass(health, strength, name); + } + + public async start() { + Logger.log('Игра началась!'); + while (this.players.length > 1) { + await this.battle(); + } + + Logger.log(`Победитель: ${this.players[0].getName()}`); + } + + private async battle() { + const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); + Logger.log(`(${fighters[0].getName()}) vs (${fighters[1].getName()})`); + + let turn = 0; + const players = [fighters[0], fighters[1]]; + const skillsUsed = [false, false]; + + while (fighters[0].getHealth() > 0 && fighters[1].getHealth() > 0) { + const attackerIndex = turn % 2; + const defenderIndex = (turn + 1) % 2; + const attacker = players[attackerIndex]; + const defender = players[defenderIndex]; + + const canUseSkill = + (attacker instanceof Knight && !skillsUsed[attackerIndex]) || + (attacker instanceof Archer && !skillsUsed[attackerIndex]) || + (attacker instanceof Vizard && !skillsUsed[attackerIndex]); + + if (defender.isAlive) { + attacker.attack(defender); + } + + if (canUseSkill && Math.random() < 0.5) { + if (attacker instanceof Knight) { + attacker.useSkill(defender); + } else if (attacker instanceof Archer) { + attacker.useSkill(defender); + } else if (attacker instanceof Vizard) { + attacker.useSkill(defender); + } + + skillsUsed[attackerIndex] = true; + } + + await this.delay(2000); + turn++; + } + + this.players = this.players.filter(player => player.isAlive); + } + + private shuffleArray(array: Player[]): Player[] { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} -class Archer extends Gamer {} +const game = new Game(4); +game.start(); diff --git a/rpgsaga/saga/tests/example.spec.ts b/rpgsaga/saga/tests/example.spec.ts index e3eacea0f..65cd0cacc 100644 --- a/rpgsaga/saga/tests/example.spec.ts +++ b/rpgsaga/saga/tests/example.spec.ts @@ -1,4 +1,4 @@ -import { taskA, taskB } from '../src/function'; +import { taskA, taskB } from '../src/funcAndClass/function'; describe('Tests taskA', () => { it('should return 6 values', () => { diff --git a/rpgsaga/saga/tests/person.spec.ts b/rpgsaga/saga/tests/person.spec.ts index 448f42e03..d2d301497 100644 --- a/rpgsaga/saga/tests/person.spec.ts +++ b/rpgsaga/saga/tests/person.spec.ts @@ -1,4 +1,4 @@ -import { Person } from '../src/person'; +import { Person } from '../src/funcAndClass/person'; describe('Person class methods tests', () => { it('Constructor test', () => { From 2bb353fdf967eac473a06c747d27aa4482f0a951 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Mon, 21 Oct 2024 18:43:46 +0300 Subject: [PATCH 04/27] saga fix --- rpgsaga/saga/src/index.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index 346777f3f..840f77d49 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -46,7 +46,7 @@ abstract class Player { } } - public allowToAttack(opponent: Player): boolean { + public allowToAttack(): boolean { return this.isAlive; } } @@ -70,7 +70,7 @@ class Knight extends Player { } public attack(opponent: Player): void { - if (this.allowToAttack(opponent)) { + if (this.allowToAttack()) { const damage = this.strength; Logger.log( `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ @@ -97,7 +97,7 @@ class Vizard extends Player { } public attack(opponent: Player): void { - if (this.allowToAttack(opponent)) { + if (this.allowToAttack()) { const damage = this.strength; Logger.log( `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ @@ -133,7 +133,7 @@ class Archer extends Player { } public attack(opponent: Player): void { - if (this.allowToAttack(opponent)) { + if (this.allowToAttack()) { let damage = this.strength; if (this.skillUsed) { damage += 2; From 9cb453e09467d7bdf832ed7eb56e4c6a09640c6a Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Thu, 24 Oct 2024 18:55:50 +0300 Subject: [PATCH 05/27] added input function, classes are distributed in folders --- rpgsaga/saga/src/abstract/Player.ts | 45 +++++ rpgsaga/saga/src/classes/Archer.ts | 33 ++++ rpgsaga/saga/src/classes/Knight.ts | 32 ++++ rpgsaga/saga/src/classes/Vizard.ts | 39 ++++ rpgsaga/saga/src/gameplay/Game.ts | 90 +++++++++ rpgsaga/saga/src/index.ts | 238 +----------------------- rpgsaga/saga/src/utils/input/input.ts | 25 +++ rpgsaga/saga/src/utils/output/Logger.ts | 14 ++ 8 files changed, 280 insertions(+), 236 deletions(-) create mode 100644 rpgsaga/saga/src/abstract/Player.ts create mode 100644 rpgsaga/saga/src/classes/Archer.ts create mode 100644 rpgsaga/saga/src/classes/Knight.ts create mode 100644 rpgsaga/saga/src/classes/Vizard.ts create mode 100644 rpgsaga/saga/src/gameplay/Game.ts create mode 100644 rpgsaga/saga/src/utils/input/input.ts create mode 100644 rpgsaga/saga/src/utils/output/Logger.ts diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts new file mode 100644 index 000000000..bfa80c647 --- /dev/null +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -0,0 +1,45 @@ +import { Logger } from '../utils/output/Logger'; + +export abstract class Player { + protected health: number; + protected strength: number; + protected name: string; + protected className: string; + isAlive: boolean = true; + + constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { + this.health = gamerHealth; + this.strength = gamerStrength; + this.name = gamerName; + } + + public getHealth(): number { + return this.health; + } + + public getStrength(): number { + return this.strength; + } + + public getName(): string { + return this.name; + } + + getClassName(): string { + return this.className; + } + + public abstract attack(opponent: Player): void; + + public takeDamage(damage: number): void { + this.health -= damage; + if (this.health <= 0) { + this.isAlive = false; + Logger.log(`(${this.getClassName()}) ${this.name} погибает`); + } + } + + public allowToAttack(): boolean { + return this.isAlive; + } +} diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts new file mode 100644 index 000000000..bad92b481 --- /dev/null +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -0,0 +1,33 @@ +import { Player } from '../abstract/Player'; +import { Logger } from '../utils/output/Logger'; + +export class Archer extends Player { + protected className: string = 'Archer'; + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + Logger.log( + `(${this.getClassName()}) ${ + this.name + } использует (Огненные стрелы) на (${opponent.getClassName()}) ${opponent.getName()}`, + ); + this.skillUsed = true; + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack()) { + let damage = this.strength; + if (this.skillUsed) { + damage += 2; + } + Logger.log( + `(${this.getClassName()}) ${ + this.name + } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } + } +} diff --git a/rpgsaga/saga/src/classes/Knight.ts b/rpgsaga/saga/src/classes/Knight.ts new file mode 100644 index 000000000..f10eb033f --- /dev/null +++ b/rpgsaga/saga/src/classes/Knight.ts @@ -0,0 +1,32 @@ +import { Player } from '../abstract/Player'; +import { Logger } from '../utils/output/Logger'; + +export class Knight extends Player { + protected className: string = 'Knight'; + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + this.skillUsed = true; + const skillDamage = this.strength * 1.3; + Logger.log( + `(${this.getClassName()}) ${ + this.name + } использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + ); + opponent.takeDamage(skillDamage); + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack()) { + const damage = this.strength; + Logger.log( + `(${this.getClassName()}) ${ + this.name + } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } + } +} diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts new file mode 100644 index 000000000..22be44f06 --- /dev/null +++ b/rpgsaga/saga/src/classes/Vizard.ts @@ -0,0 +1,39 @@ +import { Player } from '../abstract/Player'; +import { Logger } from '../utils/output/Logger'; + +export class Vizard extends Player { + protected className: string = 'Vizard'; + skillUsed: boolean = false; + + public useSkill(opponent: Player): void { + if (!this.skillUsed) { + Logger.log( + `(${this.getClassName()}) ${ + this.name + } использует (Заворожение) на (${opponent.getClassName()}) ${opponent.getName()}`, + ); + this.skillUsed = true; + } + } + + public attack(opponent: Player): void { + if (this.allowToAttack()) { + const damage = this.strength; + Logger.log( + `(${this.getClassName()}) ${ + this.name + } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + ); + opponent.takeDamage(damage); + } + } + + public takeDamage(damage: number): void { + if (this.skillUsed) { + Logger.log(`Противник не может атаковать ${this.name} из-за (Заворожения)`); + this.skillUsed = false; + } else { + super.takeDamage(damage); + } + } +} diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts new file mode 100644 index 000000000..005dd4bfb --- /dev/null +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -0,0 +1,90 @@ +import { Player } from '../abstract/Player'; +import { Knight } from '../classes/Knight'; +import { Archer } from '../classes/Archer'; +import { Vizard } from '../classes/Vizard'; +import { Logger } from '../utils/output/Logger'; + +export class Game { + private players: Player[]; + + constructor(playerCount: number) { + this.players = []; + const names = ['Эльдар', 'Артур', 'Гэндальф', 'Вильямс']; + + for (let i = 0; i < playerCount; i++) { + const name = names[Math.floor(Math.random() * names.length)]; + const health = Math.floor(Math.random() * 50) + 50; + const strength = Math.floor(Math.random() * 10) + 10; + this.players.push(this.createPlayer(name, health, strength)); + } + } + + private createPlayer(name: string, health: number, strength: number): Player { + const types = [Knight, Archer, Vizard]; + const PlayerClass = types[Math.floor(Math.random() * types.length)]; + return new PlayerClass(health, strength, name); + } + + public async start() { + Logger.log('Игра началась!'); + while (this.players.length > 1) { + await this.battle(); + } + + Logger.log(`Победитель: ${this.players[0].getName()}`); + } + + private async battle() { + const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); + Logger.log(`(${fighters[0].getName()}) vs (${fighters[1].getName()})`); + + let turn = 0; + const players = [fighters[0], fighters[1]]; + const skillsUsed = [false, false]; + + while (fighters[0].getHealth() > 0 && fighters[1].getHealth() > 0) { + const attackerIndex = turn % 2; + const defenderIndex = (turn + 1) % 2; + const attacker = players[attackerIndex]; + const defender = players[defenderIndex]; + + const canUseSkill = + (attacker instanceof Knight && !skillsUsed[attackerIndex]) || + (attacker instanceof Archer && !skillsUsed[attackerIndex]) || + (attacker instanceof Vizard && !skillsUsed[attackerIndex]); + + if (defender.isAlive) { + attacker.attack(defender); + } + + if (canUseSkill && Math.random() < 0.5) { + if (attacker instanceof Knight) { + attacker.useSkill(defender); + } else if (attacker instanceof Archer) { + attacker.useSkill(defender); + } else if (attacker instanceof Vizard) { + attacker.useSkill(defender); + } + + skillsUsed[attackerIndex] = true; + } + + await this.delay(2000); + turn++; + } + + this.players = this.players.filter(player => player.isAlive); + } + + private shuffleArray(array: Player[]): Player[] { + for (let i = array.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [array[i], array[j]] = [array[j], array[i]]; + } + return array; + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } +} diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index 840f77d49..d29e514d2 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,237 +1,3 @@ -class Logger { - private logFile: string; +import { input } from './utils/input/input'; - constructor(logFile: string = 'game_log.txt') { - this.logFile = logFile; - } - - static log(message: string): void { - const timestamp = new Date().toISOString(); - const logEntry = `${timestamp}: ${message}\n`; - console.log(logEntry); - } -} - -abstract class Player { - protected health: number; - protected strength: number; - protected name: string; - isAlive: boolean = true; - - constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { - this.health = gamerHealth; - this.strength = gamerStrength; - this.name = gamerName; - } - - public getHealth(): number { - return this.health; - } - - public getStrength(): number { - return this.strength; - } - - public getName(): string { - return this.name; - } - - public abstract attack(opponent: Player): void; - - public takeDamage(damage: number): void { - this.health -= damage; - if (this.health <= 0) { - this.isAlive = false; - Logger.log(`(${this.constructor.name}) ${this.name} погибает`); - } - } - - public allowToAttack(): boolean { - return this.isAlive; - } -} - -class Knight extends Player { - skillUsed: boolean = false; - - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - this.skillUsed = true; - const skillDamage = this.strength * 1.3; - Logger.log( - `(${this.constructor.name}) ${ - this.name - } использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - opponent.takeDamage(skillDamage); - } - } - - public attack(opponent: Player): void { - if (this.allowToAttack()) { - const damage = this.strength; - Logger.log( - `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - opponent.takeDamage(damage); - } - } -} - -class Vizard extends Player { - skillUsed: boolean = false; - - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - Logger.log( - `(${this.constructor.name}) ${this.name} использует (Заворожение) на (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - this.skillUsed = true; - } - } - - public attack(opponent: Player): void { - if (this.allowToAttack()) { - const damage = this.strength; - Logger.log( - `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - opponent.takeDamage(damage); - } - } - - public takeDamage(damage: number): void { - if (this.skillUsed) { - Logger.log(`Противник не может атаковать ${this.name} из-за (Заворожения)`); - this.skillUsed = false; - } else { - super.takeDamage(damage); - } - } -} - -class Archer extends Player { - skillUsed: boolean = false; - - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - Logger.log( - `(${this.constructor.name}) ${this.name} использует (Огненные стрелы) на (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - this.skillUsed = true; - } - } - - public attack(opponent: Player): void { - if (this.allowToAttack()) { - let damage = this.strength; - if (this.skillUsed) { - damage += 2; - } - Logger.log( - `(${this.constructor.name}) ${this.name} наносит урон ${damage} противнику (${ - opponent.constructor.name - }) ${opponent.getName()}`, - ); - opponent.takeDamage(damage); - } - } -} - -class Game { - private players: Player[]; - - constructor(playerCount: number) { - this.players = []; - const names = ['Эльдар', 'Артур', 'Гэндальф', 'Вильямс']; - - for (let i = 0; i < playerCount; i++) { - const name = names[Math.floor(Math.random() * names.length)]; - const health = Math.floor(Math.random() * 50) + 50; - const strength = Math.floor(Math.random() * 10) + 10; - this.players.push(this.createPlayer(name, health, strength)); - } - } - - private createPlayer(name: string, health: number, strength: number): Player { - const types = [Knight, Archer, Vizard]; - const PlayerClass = types[Math.floor(Math.random() * types.length)]; - return new PlayerClass(health, strength, name); - } - - public async start() { - Logger.log('Игра началась!'); - while (this.players.length > 1) { - await this.battle(); - } - - Logger.log(`Победитель: ${this.players[0].getName()}`); - } - - private async battle() { - const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); - Logger.log(`(${fighters[0].getName()}) vs (${fighters[1].getName()})`); - - let turn = 0; - const players = [fighters[0], fighters[1]]; - const skillsUsed = [false, false]; - - while (fighters[0].getHealth() > 0 && fighters[1].getHealth() > 0) { - const attackerIndex = turn % 2; - const defenderIndex = (turn + 1) % 2; - const attacker = players[attackerIndex]; - const defender = players[defenderIndex]; - - const canUseSkill = - (attacker instanceof Knight && !skillsUsed[attackerIndex]) || - (attacker instanceof Archer && !skillsUsed[attackerIndex]) || - (attacker instanceof Vizard && !skillsUsed[attackerIndex]); - - if (defender.isAlive) { - attacker.attack(defender); - } - - if (canUseSkill && Math.random() < 0.5) { - if (attacker instanceof Knight) { - attacker.useSkill(defender); - } else if (attacker instanceof Archer) { - attacker.useSkill(defender); - } else if (attacker instanceof Vizard) { - attacker.useSkill(defender); - } - - skillsUsed[attackerIndex] = true; - } - - await this.delay(2000); - turn++; - } - - this.players = this.players.filter(player => player.isAlive); - } - - private shuffleArray(array: Player[]): Player[] { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - } - - private delay(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } -} - -const game = new Game(4); -game.start(); +input(); diff --git a/rpgsaga/saga/src/utils/input/input.ts b/rpgsaga/saga/src/utils/input/input.ts new file mode 100644 index 000000000..37de745da --- /dev/null +++ b/rpgsaga/saga/src/utils/input/input.ts @@ -0,0 +1,25 @@ +import { Game } from '../../gameplay/Game'; +import * as readline from 'readline'; + +export function input(): void { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + function askForPlayers() { + rl.question('Введите число игроков (должно быть чётным): ', input => { + const number = parseInt(input); + if (isNaN(number) || number < 1 || number % 2 !== 0) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + askForPlayers(); + } else { + const game = new Game(number); + game.start(); + rl.close(); + } + }); + } + + askForPlayers(); +} diff --git a/rpgsaga/saga/src/utils/output/Logger.ts b/rpgsaga/saga/src/utils/output/Logger.ts new file mode 100644 index 000000000..313181f27 --- /dev/null +++ b/rpgsaga/saga/src/utils/output/Logger.ts @@ -0,0 +1,14 @@ +export class Logger { + //Возможность создания тектового файла + private logFile: string; + + constructor(logFile: string = 'game_log.txt') { + this.logFile = logFile; + } + + static log(message: string): void { + const timestamp = new Date().toISOString(); + const logEntry = `${timestamp}: ${message}\n`; + console.log(logEntry); + } +} From 6ec1c92919fd4ac8c4815786d74dfc3ba0fef7a7 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Thu, 24 Oct 2024 18:58:45 +0300 Subject: [PATCH 06/27] input and logger fixes --- rpgsaga/saga/src/utils/input/input.ts | 7 ++++--- rpgsaga/saga/src/utils/output/Logger.ts | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rpgsaga/saga/src/utils/input/input.ts b/rpgsaga/saga/src/utils/input/input.ts index 37de745da..989b0dddb 100644 --- a/rpgsaga/saga/src/utils/input/input.ts +++ b/rpgsaga/saga/src/utils/input/input.ts @@ -1,6 +1,7 @@ -import { Game } from '../../gameplay/Game'; import * as readline from 'readline'; +import { Game } from '../../gameplay/Game'; + export function input(): void { const rl = readline.createInterface({ input: process.stdin, @@ -8,8 +9,8 @@ export function input(): void { }); function askForPlayers() { - rl.question('Введите число игроков (должно быть чётным): ', input => { - const number = parseInt(input); + rl.question('Введите число игроков (должно быть чётным): ', inputNumber => { + const number = parseInt(inputNumber); if (isNaN(number) || number < 1 || number % 2 !== 0) { console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); askForPlayers(); diff --git a/rpgsaga/saga/src/utils/output/Logger.ts b/rpgsaga/saga/src/utils/output/Logger.ts index 313181f27..7df6a5e8d 100644 --- a/rpgsaga/saga/src/utils/output/Logger.ts +++ b/rpgsaga/saga/src/utils/output/Logger.ts @@ -1,5 +1,4 @@ export class Logger { - //Возможность создания тектового файла private logFile: string; constructor(logFile: string = 'game_log.txt') { From 36043c8404d46e297a0f4d000207cbde4decd714 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 25 Oct 2024 00:54:09 +0300 Subject: [PATCH 07/27] saga fixes + Archer, Knight and Vizard tests --- rpgsaga/saga/src/abstract/Player.ts | 48 ++++++++++++------- rpgsaga/saga/src/classes/Archer.ts | 12 ++--- rpgsaga/saga/src/classes/Knight.ts | 14 ++---- rpgsaga/saga/src/classes/Vizard.ts | 14 ++---- rpgsaga/saga/src/gameplay/Game.ts | 6 +-- rpgsaga/saga/tests/Archer.spec.ts | 71 +++++++++++++++++++++++++++ rpgsaga/saga/tests/Knight.spec.ts | 74 +++++++++++++++++++++++++++++ rpgsaga/saga/tests/Vizard.spec.ts | 70 +++++++++++++++++++++++++++ 8 files changed, 264 insertions(+), 45 deletions(-) create mode 100644 rpgsaga/saga/tests/Archer.spec.ts create mode 100644 rpgsaga/saga/tests/Knight.spec.ts create mode 100644 rpgsaga/saga/tests/Vizard.spec.ts diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index bfa80c647..dfc62ad78 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -1,41 +1,57 @@ import { Logger } from '../utils/output/Logger'; export abstract class Player { - protected health: number; - protected strength: number; - protected name: string; - protected className: string; + protected _health: number; + protected _strength: number; + protected _name: string; + protected _className: string; isAlive: boolean = true; constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { this.health = gamerHealth; this.strength = gamerStrength; - this.name = gamerName; + this._name = gamerName; } - public getHealth(): number { - return this.health; + public get health(): number { + return this._health; } - public getStrength(): number { - return this.strength; + public set health(newHP: number) { + if (newHP < 0 || newHP > 100) { + throw new Error('Недопустимый показатель здоровья'); + } else { + this._health = newHP; + } + } + + public get strength(): number { + return this._strength; + } + + public set strength(newStrength: number) { + if (newStrength < 0) { + throw new Error('Недопустимый показатель силы'); + } else { + this._strength = newStrength; + } } - public getName(): string { - return this.name; + public get name(): string { + return this._name; } - getClassName(): string { - return this.className; + public get className(): string { + return this._className; } public abstract attack(opponent: Player): void; public takeDamage(damage: number): void { - this.health -= damage; - if (this.health <= 0) { + this._health -= damage; + if (this._health <= 0) { this.isAlive = false; - Logger.log(`(${this.getClassName()}) ${this.name} погибает`); + Logger.log(`(${this.className}) ${this._name} погибает`); } } diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts index bad92b481..5fe30285c 100644 --- a/rpgsaga/saga/src/classes/Archer.ts +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -2,15 +2,13 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Archer extends Player { - protected className: string = 'Archer'; + protected _className: string = 'Archer'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { Logger.log( - `(${this.getClassName()}) ${ - this.name - } использует (Огненные стрелы) на (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} использует (Огненные стрелы) на (${opponent.className}) ${opponent.name}`, ); this.skillUsed = true; } @@ -18,14 +16,12 @@ export class Archer extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - let damage = this.strength; + let damage = this._strength; if (this.skillUsed) { damage += 2; } Logger.log( - `(${this.getClassName()}) ${ - this.name - } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, ); opponent.takeDamage(damage); } diff --git a/rpgsaga/saga/src/classes/Knight.ts b/rpgsaga/saga/src/classes/Knight.ts index f10eb033f..5f0b7f493 100644 --- a/rpgsaga/saga/src/classes/Knight.ts +++ b/rpgsaga/saga/src/classes/Knight.ts @@ -2,17 +2,15 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Knight extends Player { - protected className: string = 'Knight'; + protected _className: string = 'Knight'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { this.skillUsed = true; - const skillDamage = this.strength * 1.3; + const skillDamage = this._strength * 1.3; Logger.log( - `(${this.getClassName()}) ${ - this.name - } использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.className}) ${opponent.name}`, ); opponent.takeDamage(skillDamage); } @@ -20,11 +18,9 @@ export class Knight extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - const damage = this.strength; + const damage = this._strength; Logger.log( - `(${this.getClassName()}) ${ - this.name - } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, ); opponent.takeDamage(damage); } diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts index 22be44f06..842a9e8ee 100644 --- a/rpgsaga/saga/src/classes/Vizard.ts +++ b/rpgsaga/saga/src/classes/Vizard.ts @@ -2,15 +2,13 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Vizard extends Player { - protected className: string = 'Vizard'; + protected _className: string = 'Vizard'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { Logger.log( - `(${this.getClassName()}) ${ - this.name - } использует (Заворожение) на (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} использует (Заворожение) на (${opponent.className}) ${opponent.name}`, ); this.skillUsed = true; } @@ -18,11 +16,9 @@ export class Vizard extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - const damage = this.strength; + const damage = this._strength; Logger.log( - `(${this.getClassName()}) ${ - this.name - } наносит урон ${damage} противнику (${opponent.getClassName()}) ${opponent.getName()}`, + `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, ); opponent.takeDamage(damage); } @@ -30,7 +26,7 @@ export class Vizard extends Player { public takeDamage(damage: number): void { if (this.skillUsed) { - Logger.log(`Противник не может атаковать ${this.name} из-за (Заворожения)`); + Logger.log(`Противник не может атаковать ${this._name} из-за (Заворожения)`); this.skillUsed = false; } else { super.takeDamage(damage); diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts index 005dd4bfb..5fb4b6425 100644 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -31,18 +31,18 @@ export class Game { await this.battle(); } - Logger.log(`Победитель: ${this.players[0].getName()}`); + Logger.log(`Победитель: ${this.players[0].name}`); } private async battle() { const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); - Logger.log(`(${fighters[0].getName()}) vs (${fighters[1].getName()})`); + Logger.log(`(${fighters[0].name}) vs (${fighters[1].name})`); let turn = 0; const players = [fighters[0], fighters[1]]; const skillsUsed = [false, false]; - while (fighters[0].getHealth() > 0 && fighters[1].getHealth() > 0) { + while (fighters[0].health > 0 && fighters[1].health > 0) { const attackerIndex = turn % 2; const defenderIndex = (turn + 1) % 2; const attacker = players[attackerIndex]; diff --git a/rpgsaga/saga/tests/Archer.spec.ts b/rpgsaga/saga/tests/Archer.spec.ts new file mode 100644 index 000000000..c1b0e3e42 --- /dev/null +++ b/rpgsaga/saga/tests/Archer.spec.ts @@ -0,0 +1,71 @@ +import { Archer } from '../src/classes/Archer'; + +describe('Archer class methods tests', () => { + it('Constructor test', () => { + let newArcher = new Archer(75, 25, 'Ibragim'); + expect(newArcher.health).toEqual(75); + expect(newArcher.strength).toBe(25); + expect(newArcher.name).toBe('Ibragim'); + }); + describe('Get methods tests', () => { + let newArcher = new Archer(75, 25, 'Ibragim'); + it('Health get test', () => { + expect(newArcher.health).toEqual(75); + }); + it('Strength get test', () => { + expect(newArcher.strength).toBe(25); + }); + it('Name get test', () => { + expect(newArcher.name).toBe('Ibragim'); + }); + }); + describe('Set methods tests', () => { + let newArcher = new Archer(75, 25, 'Ibragim'); + it('Health basic test', () => { + newArcher.health = 35; + expect(newArcher.health).toEqual(35); + }); + it('Health negative test', () => { + expect(() => { + newArcher.health = -1; + }).toThrow(Error('Недопустимый показатель здоровья')); + }); + it('Strength basic test', () => { + newArcher.strength = 86; + expect(newArcher.strength).toEqual(86); + }); + it('Strength negative test', () => { + expect(() => { + newArcher.strength = -1; + }).toThrow(Error('Недопустимый показатель силы')); + }); + }); + describe('Archer methods tests', () => { + let newArcher = new Archer(75, 25, 'Ibragim'); + let opponent = new Archer(86, 26, 'Mustafa'); + it('Should change the propertie "skillUsed" to true', () => { + newArcher.useSkill(opponent); + expect(newArcher.skillUsed).toEqual(true); + }); + it('Should return health after an attack using a skill', () => { + newArcher.attack(opponent); + expect(opponent.health).toEqual(86 - (newArcher.strength + 2)); + }); + }); + describe('Archer methods tests', () => { + let newArcher = new Archer(75, 25, 'Ibragim'); + let opponent = new Archer(86, 26, 'Mustafa'); + it('Should return health after an attack whithout using a skill', () => { + newArcher.attack(opponent); + expect(opponent.health).toEqual(86 - newArcher.strength); + }); + it('Health should decrease by the number of damage units', () => { + newArcher.takeDamage(45); + expect(newArcher.health).toEqual(75 - 45); + }); + it('Ibragim should DIE.', () => { + newArcher.takeDamage(45); + expect(newArcher.isAlive).toEqual(false); + }); + }); +}); diff --git a/rpgsaga/saga/tests/Knight.spec.ts b/rpgsaga/saga/tests/Knight.spec.ts new file mode 100644 index 000000000..68d99698c --- /dev/null +++ b/rpgsaga/saga/tests/Knight.spec.ts @@ -0,0 +1,74 @@ +import { Knight } from '../src/classes/Knight'; + +describe('Knight class methods tests', () => { + it('Constructor test', () => { + let newKnight = new Knight(75, 25, 'Ibragim'); + expect(newKnight.health).toEqual(75); + expect(newKnight.strength).toBe(25); + expect(newKnight.name).toBe('Ibragim'); + }); + describe('Get methods tests', () => { + let newKnight = new Knight(75, 25, 'Ibragim'); + it('Health get test', () => { + expect(newKnight.health).toEqual(75); + }); + it('Strength get test', () => { + expect(newKnight.strength).toBe(25); + }); + it('Name get test', () => { + expect(newKnight.name).toBe('Ibragim'); + }); + }); + describe('Set methods tests', () => { + let newKnight = new Knight(75, 25, 'Ibragim'); + it('Health basic test', () => { + newKnight.health = 35; + expect(newKnight.health).toEqual(35); + }); + it('Health negative test', () => { + expect(() => { + newKnight.health = -1; + }).toThrow(Error('Недопустимый показатель здоровья')); + }); + it('Strength basic test', () => { + newKnight.strength = 86; + expect(newKnight.strength).toEqual(86); + }); + it('Strength negative test', () => { + expect(() => { + newKnight.strength = -1; + }).toThrow(Error('Недопустимый показатель силы')); + }); + }); + describe('Knight methods tests', () => { + let newKnight = new Knight(75, 25, 'Ibragim'); + let opponent = new Knight(86, 26, 'Mustafa'); + it('Should change the propertie "skillUsed" to true', () => { + newKnight.useSkill(opponent); + expect(newKnight.skillUsed).toEqual(true); + }); + it('Should return health after using a skill', () => { + expect(opponent.health).toEqual(86 - newKnight.strength * 1.3); + }); + it('Should return health after an attack using a skill', () => { + newKnight.attack(opponent); + expect(opponent.health).toEqual(86 - (newKnight.strength * 1.3 + newKnight.strength)); + }); + }); + describe('Knight methods tests', () => { + let newKnight = new Knight(75, 25, 'Ibragim'); + let opponent = new Knight(86, 26, 'Mustafa'); + it('Should return health after an attack whithout using a skill', () => { + newKnight.attack(opponent); + expect(opponent.health).toEqual(86 - newKnight.strength); + }); + it('Health should decrease by the number of damage units', () => { + newKnight.takeDamage(45); + expect(newKnight.health).toEqual(75 - 45); + }); + it('Ibragim should DIE.', () => { + newKnight.takeDamage(45); + expect(newKnight.isAlive).toEqual(false); + }); + }); +}); diff --git a/rpgsaga/saga/tests/Vizard.spec.ts b/rpgsaga/saga/tests/Vizard.spec.ts new file mode 100644 index 000000000..b4497a83c --- /dev/null +++ b/rpgsaga/saga/tests/Vizard.spec.ts @@ -0,0 +1,70 @@ +import { Vizard } from '../src/classes/Vizard'; + +describe('Vizard class methods tests', () => { + it('Constructor test', () => { + let newVizard = new Vizard(75, 25, 'Ibragim'); + expect(newVizard.health).toEqual(75); + expect(newVizard.strength).toBe(25); + expect(newVizard.name).toBe('Ibragim'); + }); + describe('Get methods tests', () => { + let newVizard = new Vizard(75, 25, 'Ibragim'); + it('Health get test', () => { + expect(newVizard.health).toEqual(75); + }); + it('Strength get test', () => { + expect(newVizard.strength).toBe(25); + }); + it('Name get test', () => { + expect(newVizard.name).toBe('Ibragim'); + }); + }); + describe('Set methods tests', () => { + let newVizard = new Vizard(75, 25, 'Ibragim'); + it('Health basic test', () => { + newVizard.health = 35; + expect(newVizard.health).toEqual(35); + }); + it('Health negative test', () => { + expect(() => { + newVizard.health = -1; + }).toThrow(Error('Недопустимый показатель здоровья')); + }); + it('Strength basic test', () => { + newVizard.strength = 86; + expect(newVizard.strength).toEqual(86); + }); + it('Strength negative test', () => { + expect(() => { + newVizard.strength = -1; + }).toThrow(Error('Недопустимый показатель силы')); + }); + }); + describe('Vizard methods tests', () => { + let newVizard = new Vizard(75, 25, 'Ibragim'); + let opponent = new Vizard(86, 26, 'Mustafa'); + it('Should change the propertie "skillUsed" to true', () => { + newVizard.useSkill(opponent); + expect(newVizard.skillUsed).toEqual(true); + }); + it('Should return health after an attack', () => { + newVizard.attack(opponent); + expect(opponent.health).toEqual(86 - newVizard.strength); + }); + it('Should change the propertie "skillUsed" to false', () => { + newVizard.takeDamage(55); + expect(newVizard.skillUsed).toEqual(false); + }); + }); + describe('Vizard methods tests', () => { + let newVizard = new Vizard(75, 25, 'Ibragim'); + it('Health should decrease by the number of damage units', () => { + newVizard.takeDamage(45); + expect(newVizard.health).toEqual(75 - 45); + }); + it('Ibragim should DIE.', () => { + newVizard.takeDamage(45); + expect(newVizard.isAlive).toEqual(false); + }); + }); +}); From 31b46c5a3fac29cc7d2d55d2920ddccbc21db955 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 25 Oct 2024 01:05:59 +0300 Subject: [PATCH 08/27] naming changes + function minor fix --- rpgsaga/saga/src/abstract/Player.ts | 40 +++++++++++------------ rpgsaga/saga/src/classes/Archer.ts | 8 ++--- rpgsaga/saga/src/classes/Knight.ts | 10 +++--- rpgsaga/saga/src/classes/Vizard.ts | 10 +++--- rpgsaga/saga/src/funcAndClass/function.ts | 14 ++------ rpgsaga/saga/src/gameplay/Game.ts | 6 ++-- rpgsaga/saga/src/index.ts | 5 +++ rpgsaga/saga/tests/Archer.spec.ts | 30 ++++++++--------- rpgsaga/saga/tests/Knight.spec.ts | 32 +++++++++--------- rpgsaga/saga/tests/Vizard.spec.ts | 28 ++++++++-------- 10 files changed, 90 insertions(+), 93 deletions(-) diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index dfc62ad78..66fabeaee 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -1,57 +1,57 @@ import { Logger } from '../utils/output/Logger'; export abstract class Player { - protected _health: number; - protected _strength: number; - protected _name: string; - protected _className: string; + protected health: number; + protected strength: number; + protected name: string; + protected className: string; isAlive: boolean = true; constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { this.health = gamerHealth; this.strength = gamerStrength; - this._name = gamerName; + this.name = gamerName; } - public get health(): number { - return this._health; + public get hp(): number { + return this.health; } - public set health(newHP: number) { + public set hp(newHP: number) { if (newHP < 0 || newHP > 100) { throw new Error('Недопустимый показатель здоровья'); } else { - this._health = newHP; + this.health = newHP; } } - public get strength(): number { - return this._strength; + public get sp(): number { + return this.strength; } - public set strength(newStrength: number) { + public set sp(newStrength: number) { if (newStrength < 0) { throw new Error('Недопустимый показатель силы'); } else { - this._strength = newStrength; + this.strength = newStrength; } } - public get name(): string { - return this._name; + public get playerName(): string { + return this.name; } - public get className(): string { - return this._className; + public get playerClassName(): string { + return this.className; } public abstract attack(opponent: Player): void; public takeDamage(damage: number): void { - this._health -= damage; - if (this._health <= 0) { + this.health -= damage; + if (this.health <= 0) { this.isAlive = false; - Logger.log(`(${this.className}) ${this._name} погибает`); + Logger.log(`(${this.className}) ${this.name} погибает`); } } diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts index 5fe30285c..f14db08a7 100644 --- a/rpgsaga/saga/src/classes/Archer.ts +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -2,13 +2,13 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Archer extends Player { - protected _className: string = 'Archer'; + protected className: string = 'Archer'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { Logger.log( - `(${this.className}) ${this._name} использует (Огненные стрелы) на (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} использует (Огненные стрелы) на (${opponent.playerClassName}) ${opponent.playerName}`, ); this.skillUsed = true; } @@ -16,12 +16,12 @@ export class Archer extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - let damage = this._strength; + let damage = this.strength; if (this.skillUsed) { damage += 2; } Logger.log( - `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, ); opponent.takeDamage(damage); } diff --git a/rpgsaga/saga/src/classes/Knight.ts b/rpgsaga/saga/src/classes/Knight.ts index 5f0b7f493..6b777bfc0 100644 --- a/rpgsaga/saga/src/classes/Knight.ts +++ b/rpgsaga/saga/src/classes/Knight.ts @@ -2,15 +2,15 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Knight extends Player { - protected _className: string = 'Knight'; + protected className: string = 'Knight'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { this.skillUsed = true; - const skillDamage = this._strength * 1.3; + const skillDamage = this.strength * 1.3; Logger.log( - `(${this.className}) ${this._name} использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, ); opponent.takeDamage(skillDamage); } @@ -18,9 +18,9 @@ export class Knight extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - const damage = this._strength; + const damage = this.strength; Logger.log( - `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, ); opponent.takeDamage(damage); } diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts index 842a9e8ee..5a677d3c7 100644 --- a/rpgsaga/saga/src/classes/Vizard.ts +++ b/rpgsaga/saga/src/classes/Vizard.ts @@ -2,13 +2,13 @@ import { Player } from '../abstract/Player'; import { Logger } from '../utils/output/Logger'; export class Vizard extends Player { - protected _className: string = 'Vizard'; + protected className: string = 'Vizard'; skillUsed: boolean = false; public useSkill(opponent: Player): void { if (!this.skillUsed) { Logger.log( - `(${this.className}) ${this._name} использует (Заворожение) на (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} использует (Заворожение) на (${opponent.playerClassName}) ${opponent.playerName}`, ); this.skillUsed = true; } @@ -16,9 +16,9 @@ export class Vizard extends Player { public attack(opponent: Player): void { if (this.allowToAttack()) { - const damage = this._strength; + const damage = this.strength; Logger.log( - `(${this.className}) ${this._name} наносит урон ${damage} противнику (${opponent.className}) ${opponent.name}`, + `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, ); opponent.takeDamage(damage); } @@ -26,7 +26,7 @@ export class Vizard extends Player { public takeDamage(damage: number): void { if (this.skillUsed) { - Logger.log(`Противник не может атаковать ${this._name} из-за (Заворожения)`); + Logger.log(`Противник не может атаковать ${this.playerName} из-за (Заворожения)`); this.skillUsed = false; } else { super.takeDamage(damage); diff --git a/rpgsaga/saga/src/funcAndClass/function.ts b/rpgsaga/saga/src/funcAndClass/function.ts index 02588ecdf..623507ff3 100644 --- a/rpgsaga/saga/src/funcAndClass/function.ts +++ b/rpgsaga/saga/src/funcAndClass/function.ts @@ -2,7 +2,7 @@ function result(a: number, x: number): number { return Math.pow(a, Math.pow(x, 2) - 1) - Math.log10(Math.pow(x, 2) - 1) + Math.cbrt(Math.pow(x, 2) - 1); } -function taskA(a: number, xBegin: number, xEnd: number, xDelta: number): number[] { +export function taskA(a: number, xBegin: number, xEnd: number, xDelta: number): number[] { const y: number[] = []; if (xDelta === 0) { return []; @@ -22,7 +22,7 @@ function taskA(a: number, xBegin: number, xEnd: number, xDelta: number): number[ return y; } -function taskB(a: number, values: number[]): number[] { +export function taskB(a: number, values: number[]): number[] { if (values.length === 0) { return []; } @@ -33,7 +33,7 @@ function taskB(a: number, values: number[]): number[] { return y; } -function output(nameOfTask: string, results: number[]): string { +export function output(nameOfTask: string, results: number[]): string { let resMsg = `Solutions to task ${nameOfTask}:\n`; results.forEach(resultNum => { @@ -41,11 +41,3 @@ function output(nameOfTask: string, results: number[]): string { }); return resMsg; } - -const taskAResult = taskA(1.6, 2, 10, 0); -console.log(output('A', taskAResult)); - -const taskBResult = taskB(1.6, []); -console.log(output('B', taskBResult)); - -export { taskA, taskB }; diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts index 5fb4b6425..cae97eab7 100644 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -31,18 +31,18 @@ export class Game { await this.battle(); } - Logger.log(`Победитель: ${this.players[0].name}`); + Logger.log(`Победитель: ${this.players[0].playerName}`); } private async battle() { const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); - Logger.log(`(${fighters[0].name}) vs (${fighters[1].name})`); + Logger.log(`(${fighters[0].playerName}) vs (${fighters[1].playerName})`); let turn = 0; const players = [fighters[0], fighters[1]]; const skillsUsed = [false, false]; - while (fighters[0].health > 0 && fighters[1].health > 0) { + while (fighters[0].hp > 0 && fighters[1].hp > 0) { const attackerIndex = turn % 2; const defenderIndex = (turn + 1) % 2; const attacker = players[attackerIndex]; diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index d29e514d2..dd786bb01 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,3 +1,8 @@ import { input } from './utils/input/input'; +import { taskA, taskB, output } from './funcAndClass/function'; + +console.log(output('A', taskA(1.6, 1.2, 3.7, 0.5))); + +console.log(output('B', taskB(1.6, [1.28, 1.36, 2.47, 3.68, 4.56]))); input(); diff --git a/rpgsaga/saga/tests/Archer.spec.ts b/rpgsaga/saga/tests/Archer.spec.ts index c1b0e3e42..7129cc231 100644 --- a/rpgsaga/saga/tests/Archer.spec.ts +++ b/rpgsaga/saga/tests/Archer.spec.ts @@ -3,40 +3,40 @@ import { Archer } from '../src/classes/Archer'; describe('Archer class methods tests', () => { it('Constructor test', () => { let newArcher = new Archer(75, 25, 'Ibragim'); - expect(newArcher.health).toEqual(75); - expect(newArcher.strength).toBe(25); - expect(newArcher.name).toBe('Ibragim'); + expect(newArcher.hp).toEqual(75); + expect(newArcher.sp).toBe(25); + expect(newArcher.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newArcher.health).toEqual(75); + expect(newArcher.hp).toEqual(75); }); it('Strength get test', () => { - expect(newArcher.strength).toBe(25); + expect(newArcher.sp).toBe(25); }); it('Name get test', () => { - expect(newArcher.name).toBe('Ibragim'); + expect(newArcher.playerName).toBe('Ibragim'); }); }); describe('Set methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); it('Health basic test', () => { - newArcher.health = 35; - expect(newArcher.health).toEqual(35); + newArcher.hp = 35; + expect(newArcher.hp).toEqual(35); }); it('Health negative test', () => { expect(() => { - newArcher.health = -1; + newArcher.hp = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newArcher.strength = 86; - expect(newArcher.strength).toEqual(86); + newArcher.sp = 86; + expect(newArcher.sp).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newArcher.strength = -1; + newArcher.sp = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -49,7 +49,7 @@ describe('Archer class methods tests', () => { }); it('Should return health after an attack using a skill', () => { newArcher.attack(opponent); - expect(opponent.health).toEqual(86 - (newArcher.strength + 2)); + expect(opponent.hp).toEqual(86 - (newArcher.sp + 2)); }); }); describe('Archer methods tests', () => { @@ -57,11 +57,11 @@ describe('Archer class methods tests', () => { let opponent = new Archer(86, 26, 'Mustafa'); it('Should return health after an attack whithout using a skill', () => { newArcher.attack(opponent); - expect(opponent.health).toEqual(86 - newArcher.strength); + expect(opponent.hp).toEqual(86 - newArcher.sp); }); it('Health should decrease by the number of damage units', () => { newArcher.takeDamage(45); - expect(newArcher.health).toEqual(75 - 45); + expect(newArcher.hp).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newArcher.takeDamage(45); diff --git a/rpgsaga/saga/tests/Knight.spec.ts b/rpgsaga/saga/tests/Knight.spec.ts index 68d99698c..709b5b3a6 100644 --- a/rpgsaga/saga/tests/Knight.spec.ts +++ b/rpgsaga/saga/tests/Knight.spec.ts @@ -3,40 +3,40 @@ import { Knight } from '../src/classes/Knight'; describe('Knight class methods tests', () => { it('Constructor test', () => { let newKnight = new Knight(75, 25, 'Ibragim'); - expect(newKnight.health).toEqual(75); - expect(newKnight.strength).toBe(25); - expect(newKnight.name).toBe('Ibragim'); + expect(newKnight.hp).toEqual(75); + expect(newKnight.sp).toBe(25); + expect(newKnight.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newKnight = new Knight(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newKnight.health).toEqual(75); + expect(newKnight.hp).toEqual(75); }); it('Strength get test', () => { - expect(newKnight.strength).toBe(25); + expect(newKnight.sp).toBe(25); }); it('Name get test', () => { - expect(newKnight.name).toBe('Ibragim'); + expect(newKnight.playerName).toBe('Ibragim'); }); }); describe('Set methods tests', () => { let newKnight = new Knight(75, 25, 'Ibragim'); it('Health basic test', () => { - newKnight.health = 35; - expect(newKnight.health).toEqual(35); + newKnight.hp = 35; + expect(newKnight.hp).toEqual(35); }); it('Health negative test', () => { expect(() => { - newKnight.health = -1; + newKnight.hp = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newKnight.strength = 86; - expect(newKnight.strength).toEqual(86); + newKnight.sp = 86; + expect(newKnight.sp).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newKnight.strength = -1; + newKnight.sp = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -48,11 +48,11 @@ describe('Knight class methods tests', () => { expect(newKnight.skillUsed).toEqual(true); }); it('Should return health after using a skill', () => { - expect(opponent.health).toEqual(86 - newKnight.strength * 1.3); + expect(opponent.hp).toEqual(86 - newKnight.sp * 1.3); }); it('Should return health after an attack using a skill', () => { newKnight.attack(opponent); - expect(opponent.health).toEqual(86 - (newKnight.strength * 1.3 + newKnight.strength)); + expect(opponent.hp).toEqual(86 - (newKnight.sp * 1.3 + newKnight.sp)); }); }); describe('Knight methods tests', () => { @@ -60,11 +60,11 @@ describe('Knight class methods tests', () => { let opponent = new Knight(86, 26, 'Mustafa'); it('Should return health after an attack whithout using a skill', () => { newKnight.attack(opponent); - expect(opponent.health).toEqual(86 - newKnight.strength); + expect(opponent.hp).toEqual(86 - newKnight.sp); }); it('Health should decrease by the number of damage units', () => { newKnight.takeDamage(45); - expect(newKnight.health).toEqual(75 - 45); + expect(newKnight.hp).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newKnight.takeDamage(45); diff --git a/rpgsaga/saga/tests/Vizard.spec.ts b/rpgsaga/saga/tests/Vizard.spec.ts index b4497a83c..2f5313208 100644 --- a/rpgsaga/saga/tests/Vizard.spec.ts +++ b/rpgsaga/saga/tests/Vizard.spec.ts @@ -3,40 +3,40 @@ import { Vizard } from '../src/classes/Vizard'; describe('Vizard class methods tests', () => { it('Constructor test', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); - expect(newVizard.health).toEqual(75); - expect(newVizard.strength).toBe(25); - expect(newVizard.name).toBe('Ibragim'); + expect(newVizard.hp).toEqual(75); + expect(newVizard.sp).toBe(25); + expect(newVizard.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newVizard.health).toEqual(75); + expect(newVizard.hp).toEqual(75); }); it('Strength get test', () => { - expect(newVizard.strength).toBe(25); + expect(newVizard.sp).toBe(25); }); it('Name get test', () => { - expect(newVizard.name).toBe('Ibragim'); + expect(newVizard.playerName).toBe('Ibragim'); }); }); describe('Set methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health basic test', () => { - newVizard.health = 35; - expect(newVizard.health).toEqual(35); + newVizard.hp = 35; + expect(newVizard.hp).toEqual(35); }); it('Health negative test', () => { expect(() => { - newVizard.health = -1; + newVizard.hp = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newVizard.strength = 86; - expect(newVizard.strength).toEqual(86); + newVizard.sp = 86; + expect(newVizard.sp).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newVizard.strength = -1; + newVizard.sp = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -49,7 +49,7 @@ describe('Vizard class methods tests', () => { }); it('Should return health after an attack', () => { newVizard.attack(opponent); - expect(opponent.health).toEqual(86 - newVizard.strength); + expect(opponent.hp).toEqual(86 - newVizard.sp); }); it('Should change the propertie "skillUsed" to false', () => { newVizard.takeDamage(55); @@ -60,7 +60,7 @@ describe('Vizard class methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health should decrease by the number of damage units', () => { newVizard.takeDamage(45); - expect(newVizard.health).toEqual(75 - 45); + expect(newVizard.hp).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newVizard.takeDamage(45); From 6367525dbf7f58a45211db3660aa32ef2001b13d Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 25 Oct 2024 20:12:38 +0300 Subject: [PATCH 09/27] saga fixes + tests --- rpgsaga/saga/src/abstract/Player.ts | 8 ++--- rpgsaga/saga/src/gameplay/Game.ts | 6 +++- rpgsaga/saga/src/index.ts | 1 + rpgsaga/saga/src/utils/output/Logger.ts | 6 ---- .../Person.spec.ts} | 2 +- .../{ => funcAndClassTests}/example.spec.ts | 2 +- .../saga/tests/{ => sagaTests}/Archer.spec.ts | 28 ++++++++--------- rpgsaga/saga/tests/sagaTests/Game.spec.ts | 27 +++++++++++++++++ .../saga/tests/{ => sagaTests}/Knight.spec.ts | 30 +++++++++---------- rpgsaga/saga/tests/sagaTests/Logger.spec.ts | 7 +++++ .../saga/tests/{ => sagaTests}/Vizard.spec.ts | 26 ++++++++-------- 11 files changed, 88 insertions(+), 55 deletions(-) rename rpgsaga/saga/tests/{person.spec.ts => funcAndClassTests/Person.spec.ts} (97%) rename rpgsaga/saga/tests/{ => funcAndClassTests}/example.spec.ts (97%) rename rpgsaga/saga/tests/{ => sagaTests}/Archer.spec.ts (72%) create mode 100644 rpgsaga/saga/tests/sagaTests/Game.spec.ts rename rpgsaga/saga/tests/{ => sagaTests}/Knight.spec.ts (70%) create mode 100644 rpgsaga/saga/tests/sagaTests/Logger.spec.ts rename rpgsaga/saga/tests/{ => sagaTests}/Vizard.spec.ts (74%) diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index 66fabeaee..92ab3e546 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -13,11 +13,11 @@ export abstract class Player { this.name = gamerName; } - public get hp(): number { + public get healthPoints(): number { return this.health; } - public set hp(newHP: number) { + public set healthPoints(newHP: number) { if (newHP < 0 || newHP > 100) { throw new Error('Недопустимый показатель здоровья'); } else { @@ -25,11 +25,11 @@ export abstract class Player { } } - public get sp(): number { + public get strengthPoints(): number { return this.strength; } - public set sp(newStrength: number) { + public set strengthPoints(newStrength: number) { if (newStrength < 0) { throw new Error('Недопустимый показатель силы'); } else { diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts index cae97eab7..8812e202d 100644 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -19,6 +19,10 @@ export class Game { } } + public get playersCount() { + return this.players; + } + private createPlayer(name: string, health: number, strength: number): Player { const types = [Knight, Archer, Vizard]; const PlayerClass = types[Math.floor(Math.random() * types.length)]; @@ -42,7 +46,7 @@ export class Game { const players = [fighters[0], fighters[1]]; const skillsUsed = [false, false]; - while (fighters[0].hp > 0 && fighters[1].hp > 0) { + while (fighters[0].healthPoints > 0 && fighters[1].healthPoints > 0) { const attackerIndex = turn % 2; const defenderIndex = (turn + 1) % 2; const attacker = players[attackerIndex]; diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index dd786bb01..3ee73e8ad 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,4 +1,5 @@ import { input } from './utils/input/input'; +import { Game } from './gameplay/Game'; import { taskA, taskB, output } from './funcAndClass/function'; console.log(output('A', taskA(1.6, 1.2, 3.7, 0.5))); diff --git a/rpgsaga/saga/src/utils/output/Logger.ts b/rpgsaga/saga/src/utils/output/Logger.ts index 7df6a5e8d..2cf2173e5 100644 --- a/rpgsaga/saga/src/utils/output/Logger.ts +++ b/rpgsaga/saga/src/utils/output/Logger.ts @@ -1,10 +1,4 @@ export class Logger { - private logFile: string; - - constructor(logFile: string = 'game_log.txt') { - this.logFile = logFile; - } - static log(message: string): void { const timestamp = new Date().toISOString(); const logEntry = `${timestamp}: ${message}\n`; diff --git a/rpgsaga/saga/tests/person.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts similarity index 97% rename from rpgsaga/saga/tests/person.spec.ts rename to rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts index d2d301497..54c4dd0e7 100644 --- a/rpgsaga/saga/tests/person.spec.ts +++ b/rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts @@ -1,4 +1,4 @@ -import { Person } from '../src/funcAndClass/person'; +import { Person } from '../../src/funcAndClass/person'; describe('Person class methods tests', () => { it('Constructor test', () => { diff --git a/rpgsaga/saga/tests/example.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts similarity index 97% rename from rpgsaga/saga/tests/example.spec.ts rename to rpgsaga/saga/tests/funcAndClassTests/example.spec.ts index 65cd0cacc..1942bf265 100644 --- a/rpgsaga/saga/tests/example.spec.ts +++ b/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts @@ -1,4 +1,4 @@ -import { taskA, taskB } from '../src/funcAndClass/function'; +import { taskA, taskB } from '../../src/funcAndClass/function'; describe('Tests taskA', () => { it('should return 6 values', () => { diff --git a/rpgsaga/saga/tests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts similarity index 72% rename from rpgsaga/saga/tests/Archer.spec.ts rename to rpgsaga/saga/tests/sagaTests/Archer.spec.ts index 7129cc231..1935d25ad 100644 --- a/rpgsaga/saga/tests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -1,19 +1,19 @@ -import { Archer } from '../src/classes/Archer'; +import { Archer } from '../../src/classes/Archer'; describe('Archer class methods tests', () => { it('Constructor test', () => { let newArcher = new Archer(75, 25, 'Ibragim'); - expect(newArcher.hp).toEqual(75); - expect(newArcher.sp).toBe(25); + expect(newArcher.healthPoints).toEqual(75); + expect(newArcher.strengthPoints).toBe(25); expect(newArcher.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newArcher.hp).toEqual(75); + expect(newArcher.healthPoints).toEqual(75); }); it('Strength get test', () => { - expect(newArcher.sp).toBe(25); + expect(newArcher.strengthPoints).toBe(25); }); it('Name get test', () => { expect(newArcher.playerName).toBe('Ibragim'); @@ -22,21 +22,21 @@ describe('Archer class methods tests', () => { describe('Set methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); it('Health basic test', () => { - newArcher.hp = 35; - expect(newArcher.hp).toEqual(35); + newArcher.healthPoints = 35; + expect(newArcher.healthPoints).toEqual(35); }); it('Health negative test', () => { expect(() => { - newArcher.hp = -1; + newArcher.healthPoints = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newArcher.sp = 86; - expect(newArcher.sp).toEqual(86); + newArcher.strengthPoints = 86; + expect(newArcher.strengthPoints).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newArcher.sp = -1; + newArcher.strengthPoints = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -49,7 +49,7 @@ describe('Archer class methods tests', () => { }); it('Should return health after an attack using a skill', () => { newArcher.attack(opponent); - expect(opponent.hp).toEqual(86 - (newArcher.sp + 2)); + expect(opponent.healthPoints).toEqual(86 - (newArcher.strengthPoints + 2)); }); }); describe('Archer methods tests', () => { @@ -57,11 +57,11 @@ describe('Archer class methods tests', () => { let opponent = new Archer(86, 26, 'Mustafa'); it('Should return health after an attack whithout using a skill', () => { newArcher.attack(opponent); - expect(opponent.hp).toEqual(86 - newArcher.sp); + expect(opponent.healthPoints).toEqual(86 - newArcher.strengthPoints); }); it('Health should decrease by the number of damage units', () => { newArcher.takeDamage(45); - expect(newArcher.hp).toEqual(75 - 45); + expect(newArcher.healthPoints).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newArcher.takeDamage(45); diff --git a/rpgsaga/saga/tests/sagaTests/Game.spec.ts b/rpgsaga/saga/tests/sagaTests/Game.spec.ts new file mode 100644 index 000000000..467972506 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/Game.spec.ts @@ -0,0 +1,27 @@ +import { Game } from '../../src/gameplay/Game'; + +describe('Game class methods tests', () => { + it('Constructor test', () => { + let newGame = new Game(4); + expect(newGame.playersCount.length).toEqual(4); + }); + it('should create players with random names, health, and strength', () => { + let newGame = new Game(2); + expect( + newGame.playersCount.every(p => p.playerName !== undefined && p.healthPoints > 0 && p.strengthPoints > 0), + ).toBe(true); + }); + it('should create players of different types (Knight, Archer, Vizard)', () => { + let newGame = new Game(10); + const playerTypes = newGame.playersCount.map(p => p.constructor.name); + expect(playerTypes.includes('Knight')).toBe(true); + expect(playerTypes.includes('Archer')).toBe(true); + expect(playerTypes.includes('Vizard')).toBe(true); + }); + describe('Get methods tests', () => { + let newGame = new Game(20); + it('Players get test', () => { + expect(newGame.playersCount.length).toEqual(20); + }); + }); +}); diff --git a/rpgsaga/saga/tests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts similarity index 70% rename from rpgsaga/saga/tests/Knight.spec.ts rename to rpgsaga/saga/tests/sagaTests/Knight.spec.ts index 709b5b3a6..c0d922fda 100644 --- a/rpgsaga/saga/tests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -1,19 +1,19 @@ -import { Knight } from '../src/classes/Knight'; +import { Knight } from '../../src/classes/Knight'; describe('Knight class methods tests', () => { it('Constructor test', () => { let newKnight = new Knight(75, 25, 'Ibragim'); - expect(newKnight.hp).toEqual(75); - expect(newKnight.sp).toBe(25); + expect(newKnight.healthPoints).toEqual(75); + expect(newKnight.strengthPoints).toBe(25); expect(newKnight.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newKnight = new Knight(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newKnight.hp).toEqual(75); + expect(newKnight.healthPoints).toEqual(75); }); it('Strength get test', () => { - expect(newKnight.sp).toBe(25); + expect(newKnight.strengthPoints).toBe(25); }); it('Name get test', () => { expect(newKnight.playerName).toBe('Ibragim'); @@ -22,21 +22,21 @@ describe('Knight class methods tests', () => { describe('Set methods tests', () => { let newKnight = new Knight(75, 25, 'Ibragim'); it('Health basic test', () => { - newKnight.hp = 35; - expect(newKnight.hp).toEqual(35); + newKnight.healthPoints = 35; + expect(newKnight.healthPoints).toEqual(35); }); it('Health negative test', () => { expect(() => { - newKnight.hp = -1; + newKnight.healthPoints = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newKnight.sp = 86; - expect(newKnight.sp).toEqual(86); + newKnight.strengthPoints = 86; + expect(newKnight.strengthPoints).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newKnight.sp = -1; + newKnight.strengthPoints = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -48,11 +48,11 @@ describe('Knight class methods tests', () => { expect(newKnight.skillUsed).toEqual(true); }); it('Should return health after using a skill', () => { - expect(opponent.hp).toEqual(86 - newKnight.sp * 1.3); + expect(opponent.healthPoints).toEqual(86 - newKnight.strengthPoints * 1.3); }); it('Should return health after an attack using a skill', () => { newKnight.attack(opponent); - expect(opponent.hp).toEqual(86 - (newKnight.sp * 1.3 + newKnight.sp)); + expect(opponent.healthPoints).toEqual(86 - (newKnight.strengthPoints * 1.3 + newKnight.strengthPoints)); }); }); describe('Knight methods tests', () => { @@ -60,11 +60,11 @@ describe('Knight class methods tests', () => { let opponent = new Knight(86, 26, 'Mustafa'); it('Should return health after an attack whithout using a skill', () => { newKnight.attack(opponent); - expect(opponent.hp).toEqual(86 - newKnight.sp); + expect(opponent.healthPoints).toEqual(86 - newKnight.strengthPoints); }); it('Health should decrease by the number of damage units', () => { newKnight.takeDamage(45); - expect(newKnight.hp).toEqual(75 - 45); + expect(newKnight.healthPoints).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newKnight.takeDamage(45); diff --git a/rpgsaga/saga/tests/sagaTests/Logger.spec.ts b/rpgsaga/saga/tests/sagaTests/Logger.spec.ts new file mode 100644 index 000000000..da7abbfb7 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/Logger.spec.ts @@ -0,0 +1,7 @@ +import { Logger } from '../../src/utils/output/Logger'; + +describe('Logger class methods tests', () => { + it('method log test', () => { + expect(Logger.log('Игра началась!')).toEqual(console.log('Игра началась!')); + }); +}); diff --git a/rpgsaga/saga/tests/Vizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts similarity index 74% rename from rpgsaga/saga/tests/Vizard.spec.ts rename to rpgsaga/saga/tests/sagaTests/Vizard.spec.ts index 2f5313208..529049115 100644 --- a/rpgsaga/saga/tests/Vizard.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts @@ -1,19 +1,19 @@ -import { Vizard } from '../src/classes/Vizard'; +import { Vizard } from '../../src/classes/Vizard'; describe('Vizard class methods tests', () => { it('Constructor test', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); - expect(newVizard.hp).toEqual(75); - expect(newVizard.sp).toBe(25); + expect(newVizard.healthPoints).toEqual(75); + expect(newVizard.strengthPoints).toBe(25); expect(newVizard.playerName).toBe('Ibragim'); }); describe('Get methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health get test', () => { - expect(newVizard.hp).toEqual(75); + expect(newVizard.healthPoints).toEqual(75); }); it('Strength get test', () => { - expect(newVizard.sp).toBe(25); + expect(newVizard.strengthPoints).toBe(25); }); it('Name get test', () => { expect(newVizard.playerName).toBe('Ibragim'); @@ -22,21 +22,21 @@ describe('Vizard class methods tests', () => { describe('Set methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health basic test', () => { - newVizard.hp = 35; - expect(newVizard.hp).toEqual(35); + newVizard.healthPoints = 35; + expect(newVizard.healthPoints).toEqual(35); }); it('Health negative test', () => { expect(() => { - newVizard.hp = -1; + newVizard.healthPoints = -1; }).toThrow(Error('Недопустимый показатель здоровья')); }); it('Strength basic test', () => { - newVizard.sp = 86; - expect(newVizard.sp).toEqual(86); + newVizard.strengthPoints = 86; + expect(newVizard.strengthPoints).toEqual(86); }); it('Strength negative test', () => { expect(() => { - newVizard.sp = -1; + newVizard.strengthPoints = -1; }).toThrow(Error('Недопустимый показатель силы')); }); }); @@ -49,7 +49,7 @@ describe('Vizard class methods tests', () => { }); it('Should return health after an attack', () => { newVizard.attack(opponent); - expect(opponent.hp).toEqual(86 - newVizard.sp); + expect(opponent.healthPoints).toEqual(86 - newVizard.strengthPoints); }); it('Should change the propertie "skillUsed" to false', () => { newVizard.takeDamage(55); @@ -60,7 +60,7 @@ describe('Vizard class methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); it('Health should decrease by the number of damage units', () => { newVizard.takeDamage(45); - expect(newVizard.hp).toEqual(75 - 45); + expect(newVizard.healthPoints).toEqual(75 - 45); }); it('Ibragim should DIE.', () => { newVizard.takeDamage(45); From d25fa7da8a5ff39308421563208daff79f9385f6 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 25 Oct 2024 20:13:48 +0300 Subject: [PATCH 10/27] minor fix --- rpgsaga/saga/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index 3ee73e8ad..dd786bb01 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,5 +1,4 @@ import { input } from './utils/input/input'; -import { Game } from './gameplay/Game'; import { taskA, taskB, output } from './funcAndClass/function'; console.log(output('A', taskA(1.6, 1.2, 3.7, 0.5))); From 3916798104c5a603d2e9ccf8e694b2ed36fb44b7 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Tue, 29 Oct 2024 02:04:15 +0300 Subject: [PATCH 11/27] randomization functions have been added --- .../utils/randomization/getRandomArrayElement.ts | 7 +++++++ .../saga/src/utils/randomization/getRandomNumber.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts create mode 100644 rpgsaga/saga/src/utils/randomization/getRandomNumber.ts diff --git a/rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts b/rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts new file mode 100644 index 000000000..ed9fcd7e1 --- /dev/null +++ b/rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts @@ -0,0 +1,7 @@ +export function getRandomArrayElement(arr: T[]): T | undefined { + if (arr.length === 0) { + return undefined; + } + const randomIndex = Math.floor(Math.random() * arr.length); + return arr[randomIndex]; +} diff --git a/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts b/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts new file mode 100644 index 000000000..66da01837 --- /dev/null +++ b/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts @@ -0,0 +1,13 @@ +export function getRandomNumber(min: number, max: number): number { + if (min > max) { + throw new Error('Minimum value cannot be greater than maximum value.'); + } + if (min === max) { + return min; + } + + const range = max - min + 1; + const randomNumber = Math.floor(Math.random() * range) + min; + + return randomNumber; +} From db9f0397504b11d70182ede8b4257f2060b34f1c Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Tue, 29 Oct 2024 02:52:56 +0300 Subject: [PATCH 12/27] the interface for skills has been added, the choice of opponents through tournaments has been added, the functionality of classes has been changed --- rpgsaga/saga/src/abstract/Player.ts | 62 +++++++---- rpgsaga/saga/src/classes/Archer.ts | 52 ++++++---- rpgsaga/saga/src/classes/Knight.ts | 38 +++---- rpgsaga/saga/src/classes/Vizard.ts | 44 +++++--- rpgsaga/saga/src/gameplay/Game.ts | 108 ++++++++++++++------ rpgsaga/saga/src/skills/ISkills.ts | 9 ++ rpgsaga/saga/tests/sagaTests/Archer.spec.ts | 25 +---- rpgsaga/saga/tests/sagaTests/Knight.spec.ts | 25 +---- rpgsaga/saga/tests/sagaTests/Vizard.spec.ts | 27 +---- 9 files changed, 214 insertions(+), 176 deletions(-) create mode 100644 rpgsaga/saga/src/skills/ISkills.ts diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index 92ab3e546..24c5dc82e 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -1,11 +1,15 @@ -import { Logger } from '../utils/output/Logger'; +import { ISkills } from '../skills/ISkills'; +import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; export abstract class Player { protected health: number; protected strength: number; protected name: string; protected className: string; - isAlive: boolean = true; + protected isAlive: boolean = true; + protected skillUsed: boolean = false; + protected skills: ISkills[] = []; + protected isCharmed: boolean = false; constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { this.health = gamerHealth; @@ -17,24 +21,16 @@ export abstract class Player { return this.health; } - public set healthPoints(newHP: number) { - if (newHP < 0 || newHP > 100) { - throw new Error('Недопустимый показатель здоровья'); - } else { - this.health = newHP; - } + public set healthPoints(newHealthPoints: number) { + this.health = newHealthPoints; } public get strengthPoints(): number { return this.strength; } - public set strengthPoints(newStrength: number) { - if (newStrength < 0) { - throw new Error('Недопустимый показатель силы'); - } else { - this.strength = newStrength; - } + public set strengthPoints(newStrengthPoints: number) { + this.strength = newStrengthPoints; } public get playerName(): string { @@ -45,17 +41,45 @@ export abstract class Player { return this.className; } - public abstract attack(opponent: Player): void; + public get isAlivePlayer(): boolean { + return this.isAlive; + } + + public get playerSkillUsed(): boolean { + return this.skillUsed; + } + + public addSkill(skill: ISkills): void { + this.skills.push(skill); + } - public takeDamage(damage: number): void { + public abstract attack(opponent: Player): string; + + public useSkill(opponent: Player): string | null { + if (this.skills.length === 0) return null; + + const availableSkills = this.skills.filter(skill => skill.isAvailable); + if (availableSkills.length === 0) return null; + + const skill = getRandomArrayElement(availableSkills); + this.skillUsed = true; + const damageDealt = skill?.effect(opponent); + let message = `(${this.playerClassName}) ${this.playerName} использует (${skill.name}) на (${opponent.playerClassName}) ${opponent.playerName}`; + if (damageDealt > 0) { + message += ` и наносит урон ${damageDealt}`; + } + return message; + } + + public takeDamage(damage: number): string { this.health -= damage; if (this.health <= 0) { this.isAlive = false; - Logger.log(`(${this.className}) ${this.name} погибает`); + return `(${this.playerClassName}) ${this.playerName} погибает`; } } - public allowToAttack(): boolean { - return this.isAlive; + public gettingCharmed(value: boolean): void { + this.isCharmed = value; } } diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts index f14db08a7..b89ed3463 100644 --- a/rpgsaga/saga/src/classes/Archer.ts +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -1,29 +1,43 @@ import { Player } from '../abstract/Player'; -import { Logger } from '../utils/output/Logger'; +import { ISkills } from '../skills/ISkills'; export class Archer extends Player { protected className: string = 'Archer'; - skillUsed: boolean = false; + protected skill: ISkills; - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - Logger.log( - `(${this.playerClassName}) ${this.playerName} использует (Огненные стрелы) на (${opponent.playerClassName}) ${opponent.playerName}`, - ); - this.skillUsed = true; - } + constructor(health: number, strength: number, name: string) { + super(health, strength, name); + this.addSkill({ + name: 'Огненные стрелы', + damage: 2, + isAvailable: true, + effect: opponent => { + if (opponent.playerClassName === 'Knight') { + return 0; + } else { + this.strength += 2; + return 0; + } + }, + }); + this.addSkill({ + name: 'Ледяные стрелы', + damage: 3, + isAvailable: true, + effect: () => { + this.strength += 3; + return 0; + }, + }); } - public attack(opponent: Player): void { - if (this.allowToAttack()) { - let damage = this.strength; - if (this.skillUsed) { - damage += 2; - } - Logger.log( - `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, - ); - opponent.takeDamage(damage); + public attack(opponent: Player): string { + if (this.isAlivePlayer && !this.isCharmed) { + opponent.takeDamage(this.strength); + return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; + } else if (this.isAlivePlayer && this.isCharmed) { + this.gettingCharmed(false); + return opponent.takeDamage(this.strength); } } } diff --git a/rpgsaga/saga/src/classes/Knight.ts b/rpgsaga/saga/src/classes/Knight.ts index 6b777bfc0..ce8855383 100644 --- a/rpgsaga/saga/src/classes/Knight.ts +++ b/rpgsaga/saga/src/classes/Knight.ts @@ -1,28 +1,30 @@ import { Player } from '../abstract/Player'; -import { Logger } from '../utils/output/Logger'; +import { ISkills } from '../skills/ISkills'; export class Knight extends Player { protected className: string = 'Knight'; - skillUsed: boolean = false; + protected skill: ISkills; - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - this.skillUsed = true; - const skillDamage = this.strength * 1.3; - Logger.log( - `(${this.playerClassName}) ${this.playerName} использует (Удар возмездия) и наносит урон ${skillDamage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, - ); - opponent.takeDamage(skillDamage); - } + constructor(health: number, strength: number, name: string) { + super(health, strength, name); + this.addSkill({ + name: 'Удар возмездия', + isAvailable: true, + effect: opponent => { + const skillDamage = this.strength * 1.3; + opponent.takeDamage(skillDamage); + return skillDamage; + }, + }); } - public attack(opponent: Player): void { - if (this.allowToAttack()) { - const damage = this.strength; - Logger.log( - `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, - ); - opponent.takeDamage(damage); + public attack(opponent: Player): string { + if (this.isAlivePlayer && !this.isCharmed) { + opponent.takeDamage(this.strength); + return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; + } else if (this.isAlivePlayer && this.isCharmed) { + this.gettingCharmed(false); + return opponent.takeDamage(this.strength); } } } diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts index 5a677d3c7..8acce1674 100644 --- a/rpgsaga/saga/src/classes/Vizard.ts +++ b/rpgsaga/saga/src/classes/Vizard.ts @@ -1,33 +1,43 @@ import { Player } from '../abstract/Player'; +import { ISkills } from '../skills/ISkills'; import { Logger } from '../utils/output/Logger'; export class Vizard extends Player { protected className: string = 'Vizard'; - skillUsed: boolean = false; + protected countOfSkills: number = 0; + protected skill: ISkills; - public useSkill(opponent: Player): void { - if (!this.skillUsed) { - Logger.log( - `(${this.playerClassName}) ${this.playerName} использует (Заворожение) на (${opponent.playerClassName}) ${opponent.playerName}`, - ); - this.skillUsed = true; - } + public get countOfVizardSkills() { + return this.countOfSkills; + } + + constructor(health: number, strength: number, name: string) { + super(health, strength, name); + this.addSkill({ + name: 'Заворожение', + isAvailable: true, + effect: opponent => { + opponent.gettingCharmed(true); + return 0; + }, + }); } - public attack(opponent: Player): void { - if (this.allowToAttack()) { - const damage = this.strength; - Logger.log( - `(${this.playerClassName}) ${this.playerName} наносит урон ${damage} противнику (${opponent.playerClassName}) ${opponent.playerName}`, - ); - opponent.takeDamage(damage); + public attack(opponent: Player): string { + if (this.isAlivePlayer && !this.isCharmed) { + opponent.takeDamage(this.strength); + return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; + } else if (this.isAlivePlayer && this.isCharmed) { + this.gettingCharmed(false); + return opponent.takeDamage(this.strength); } } - public takeDamage(damage: number): void { + public takeDamage(damage: number): string { if (this.skillUsed) { - Logger.log(`Противник не может атаковать ${this.playerName} из-за (Заворожения)`); this.skillUsed = false; + this.countOfSkills += 1; + return `Противник не может атаковать ${this.playerName} из-за (Заворожения)`; } else { super.takeDamage(damage); } diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts index 8812e202d..60beb7903 100644 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -3,19 +3,48 @@ import { Knight } from '../classes/Knight'; import { Archer } from '../classes/Archer'; import { Vizard } from '../classes/Vizard'; import { Logger } from '../utils/output/Logger'; +import { getRandomNumber } from '../utils/randomization/getRandomNumber'; +import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; export class Game { - private players: Player[]; + private players: Player[] = []; + private initialHealth: number[] = []; + private initialStrength: number[] = []; constructor(playerCount: number) { this.players = []; - const names = ['Эльдар', 'Артур', 'Гэндальф', 'Вильямс']; + const names = [ + 'Эльдар', + 'Артур', + 'Гэндальф', + 'Вильямс', + 'Агатон', + 'Аполлон', + 'Артемида', + 'Зевс', + 'Персей', + 'Феникс', + 'Элита', + 'Ирида', + 'Медея', + 'Орион', + 'Рафаэль', + 'Себастиан', + 'Эмиль', + 'Аврора', + 'Веста', + 'Лилия', + 'Мира', + ]; for (let i = 0; i < playerCount; i++) { - const name = names[Math.floor(Math.random() * names.length)]; - const health = Math.floor(Math.random() * 50) + 50; - const strength = Math.floor(Math.random() * 10) + 10; - this.players.push(this.createPlayer(name, health, strength)); + const name = getRandomArrayElement(names); + const health = getRandomNumber(75, 100); + const strength = getRandomNumber(15, 20); + const player = this.createPlayer(name, health, strength); + this.players.push(player); + this.initialHealth.push(player.healthPoints); + this.initialStrength.push(player.strengthPoints); } } @@ -25,59 +54,72 @@ export class Game { private createPlayer(name: string, health: number, strength: number): Player { const types = [Knight, Archer, Vizard]; - const PlayerClass = types[Math.floor(Math.random() * types.length)]; + const PlayerClass = getRandomArrayElement(types); return new PlayerClass(health, strength, name); } public async start() { Logger.log('Игра началась!'); - while (this.players.length > 1) { - await this.battle(); + this.players = this.shuffleArray(this.players); + await this.tournament(this.players); + Logger.log(`Победитель: ${this.players[0].playerName}`); + } + + private async tournament(players: Player[]): Promise { + if (players.length === 1) { + return players[0]; } - Logger.log(`Победитель: ${this.players[0].playerName}`); + const nextRoundPlayers: Player[] = []; + for (let i = 0; i < players.length; i += 2) { + const player1 = players[i]; + const player2 = players[i + 1]; + const winner = await this.battle([player1, player2]); + nextRoundPlayers.push(winner); + player1.healthPoints = this.initialHealth[this.players.indexOf(player1)]; + player2.healthPoints = this.initialHealth[this.players.indexOf(player2)]; + player1.strengthPoints = this.initialStrength[this.players.indexOf(player1)]; + player2.strengthPoints = this.initialStrength[this.players.indexOf(player2)]; + } + + return this.tournament(nextRoundPlayers); } - private async battle() { - const fighters: Player[] = this.shuffleArray(this.players).slice(0, 2); + private async battle(fighters: Player[]): Promise { Logger.log(`(${fighters[0].playerName}) vs (${fighters[1].playerName})`); let turn = 0; - const players = [fighters[0], fighters[1]]; - const skillsUsed = [false, false]; while (fighters[0].healthPoints > 0 && fighters[1].healthPoints > 0) { const attackerIndex = turn % 2; const defenderIndex = (turn + 1) % 2; - const attacker = players[attackerIndex]; - const defender = players[defenderIndex]; + const attacker = fighters[attackerIndex]; + const defender = fighters[defenderIndex]; - const canUseSkill = - (attacker instanceof Knight && !skillsUsed[attackerIndex]) || - (attacker instanceof Archer && !skillsUsed[attackerIndex]) || - (attacker instanceof Vizard && !skillsUsed[attackerIndex]); + if (defender.isAlivePlayer) { + Logger.log(attacker.attack(defender)); - if (defender.isAlive) { - attacker.attack(defender); + if (!defender.isAlivePlayer) { + Logger.log(defender.takeDamage(1)); + break; + } } - if (canUseSkill && Math.random() < 0.5) { - if (attacker instanceof Knight) { - attacker.useSkill(defender); - } else if (attacker instanceof Archer) { - attacker.useSkill(defender); - } else if (attacker instanceof Vizard) { - attacker.useSkill(defender); + if (Math.random() < 0.5 && attacker.isAlivePlayer && defender.isAlivePlayer) { + if (attacker instanceof Knight && !attacker.playerSkillUsed) { + Logger.log(attacker.useSkill(defender)); + } else if (attacker instanceof Archer && !attacker.playerSkillUsed) { + Logger.log(attacker.useSkill(defender)); + } else if (attacker instanceof Vizard && !attacker.playerSkillUsed && attacker.countOfVizardSkills < 1) { + Logger.log(attacker.useSkill(defender)); } - - skillsUsed[attackerIndex] = true; } - await this.delay(2000); + await this.delay(200); turn++; } - this.players = this.players.filter(player => player.isAlive); + return fighters.find(player => player.healthPoints > 0)!; } private shuffleArray(array: Player[]): Player[] { diff --git a/rpgsaga/saga/src/skills/ISkills.ts b/rpgsaga/saga/src/skills/ISkills.ts new file mode 100644 index 000000000..66689dcb8 --- /dev/null +++ b/rpgsaga/saga/src/skills/ISkills.ts @@ -0,0 +1,9 @@ +import { Player } from '../abstract/Player'; + +export interface ISkills { + name: string; + damage?: number; + turns?: number; + isAvailable: boolean; + effect?: (opponent: Player) => number; +} diff --git a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts index 1935d25ad..64c67af65 100644 --- a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -19,33 +19,12 @@ describe('Archer class methods tests', () => { expect(newArcher.playerName).toBe('Ibragim'); }); }); - describe('Set methods tests', () => { - let newArcher = new Archer(75, 25, 'Ibragim'); - it('Health basic test', () => { - newArcher.healthPoints = 35; - expect(newArcher.healthPoints).toEqual(35); - }); - it('Health negative test', () => { - expect(() => { - newArcher.healthPoints = -1; - }).toThrow(Error('Недопустимый показатель здоровья')); - }); - it('Strength basic test', () => { - newArcher.strengthPoints = 86; - expect(newArcher.strengthPoints).toEqual(86); - }); - it('Strength negative test', () => { - expect(() => { - newArcher.strengthPoints = -1; - }).toThrow(Error('Недопустимый показатель силы')); - }); - }); describe('Archer methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); let opponent = new Archer(86, 26, 'Mustafa'); it('Should change the propertie "skillUsed" to true', () => { newArcher.useSkill(opponent); - expect(newArcher.skillUsed).toEqual(true); + expect(newArcher.playerSkillUsed).toEqual(true); }); it('Should return health after an attack using a skill', () => { newArcher.attack(opponent); @@ -65,7 +44,7 @@ describe('Archer class methods tests', () => { }); it('Ibragim should DIE.', () => { newArcher.takeDamage(45); - expect(newArcher.isAlive).toEqual(false); + expect(newArcher.isAlivePlayer).toEqual(false); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts index c0d922fda..d356e9240 100644 --- a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -19,33 +19,12 @@ describe('Knight class methods tests', () => { expect(newKnight.playerName).toBe('Ibragim'); }); }); - describe('Set methods tests', () => { - let newKnight = new Knight(75, 25, 'Ibragim'); - it('Health basic test', () => { - newKnight.healthPoints = 35; - expect(newKnight.healthPoints).toEqual(35); - }); - it('Health negative test', () => { - expect(() => { - newKnight.healthPoints = -1; - }).toThrow(Error('Недопустимый показатель здоровья')); - }); - it('Strength basic test', () => { - newKnight.strengthPoints = 86; - expect(newKnight.strengthPoints).toEqual(86); - }); - it('Strength negative test', () => { - expect(() => { - newKnight.strengthPoints = -1; - }).toThrow(Error('Недопустимый показатель силы')); - }); - }); describe('Knight methods tests', () => { let newKnight = new Knight(75, 25, 'Ibragim'); let opponent = new Knight(86, 26, 'Mustafa'); it('Should change the propertie "skillUsed" to true', () => { newKnight.useSkill(opponent); - expect(newKnight.skillUsed).toEqual(true); + expect(newKnight.playerSkillUsed).toEqual(true); }); it('Should return health after using a skill', () => { expect(opponent.healthPoints).toEqual(86 - newKnight.strengthPoints * 1.3); @@ -68,7 +47,7 @@ describe('Knight class methods tests', () => { }); it('Ibragim should DIE.', () => { newKnight.takeDamage(45); - expect(newKnight.isAlive).toEqual(false); + expect(newKnight.isAlivePlayer).toEqual(false); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts index 529049115..e1a62f79c 100644 --- a/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts @@ -19,33 +19,12 @@ describe('Vizard class methods tests', () => { expect(newVizard.playerName).toBe('Ibragim'); }); }); - describe('Set methods tests', () => { - let newVizard = new Vizard(75, 25, 'Ibragim'); - it('Health basic test', () => { - newVizard.healthPoints = 35; - expect(newVizard.healthPoints).toEqual(35); - }); - it('Health negative test', () => { - expect(() => { - newVizard.healthPoints = -1; - }).toThrow(Error('Недопустимый показатель здоровья')); - }); - it('Strength basic test', () => { - newVizard.strengthPoints = 86; - expect(newVizard.strengthPoints).toEqual(86); - }); - it('Strength negative test', () => { - expect(() => { - newVizard.strengthPoints = -1; - }).toThrow(Error('Недопустимый показатель силы')); - }); - }); describe('Vizard methods tests', () => { let newVizard = new Vizard(75, 25, 'Ibragim'); let opponent = new Vizard(86, 26, 'Mustafa'); it('Should change the propertie "skillUsed" to true', () => { newVizard.useSkill(opponent); - expect(newVizard.skillUsed).toEqual(true); + expect(newVizard.playerSkillUsed).toEqual(true); }); it('Should return health after an attack', () => { newVizard.attack(opponent); @@ -53,7 +32,7 @@ describe('Vizard class methods tests', () => { }); it('Should change the propertie "skillUsed" to false', () => { newVizard.takeDamage(55); - expect(newVizard.skillUsed).toEqual(false); + expect(newVizard.playerSkillUsed).toEqual(false); }); }); describe('Vizard methods tests', () => { @@ -64,7 +43,7 @@ describe('Vizard class methods tests', () => { }); it('Ibragim should DIE.', () => { newVizard.takeDamage(45); - expect(newVizard.isAlive).toEqual(false); + expect(newVizard.isAlivePlayer).toEqual(false); }); }); }); From 158db7b729a5737ddcbee62117dd4ea4a7bfdf42 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Tue, 29 Oct 2024 14:45:48 +0300 Subject: [PATCH 13/27] saga fixes --- rpgsaga/saga/src/abstract/Player.ts | 6 +++++- rpgsaga/saga/src/classes/Archer.ts | 25 +++++++++++++++++++++++++ rpgsaga/saga/src/classes/Vizard.ts | 7 +++++-- rpgsaga/saga/src/gameplay/Game.ts | 16 +++++++--------- rpgsaga/saga/src/skills/ISkills.ts | 2 +- rpgsaga/saga/src/utils/input/input.ts | 4 ++-- 6 files changed, 45 insertions(+), 15 deletions(-) diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index 24c5dc82e..e78e85d94 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -49,6 +49,10 @@ export abstract class Player { return this.skillUsed; } + public set playerSkillUsed(value: boolean) { + this.skillUsed = value; + } + public addSkill(skill: ISkills): void { this.skills.push(skill); } @@ -63,7 +67,7 @@ export abstract class Player { const skill = getRandomArrayElement(availableSkills); this.skillUsed = true; - const damageDealt = skill?.effect(opponent); + const damageDealt = skill.effect(opponent); let message = `(${this.playerClassName}) ${this.playerName} использует (${skill.name}) на (${opponent.playerClassName}) ${opponent.playerName}`; if (damageDealt > 0) { message += ` и наносит урон ${damageDealt}`; diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts index b89ed3463..d40758ac8 100644 --- a/rpgsaga/saga/src/classes/Archer.ts +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -1,9 +1,11 @@ import { Player } from '../abstract/Player'; import { ISkills } from '../skills/ISkills'; +import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; export class Archer extends Player { protected className: string = 'Archer'; protected skill: ISkills; + protected skillBuff: number = 0; constructor(health: number, strength: number, name: string) { super(health, strength, name); @@ -23,6 +25,7 @@ export class Archer extends Player { this.addSkill({ name: 'Ледяные стрелы', damage: 3, + turns: 3, isAvailable: true, effect: () => { this.strength += 3; @@ -31,8 +34,30 @@ export class Archer extends Player { }); } + public useSkill(opponent: Player): string | null { + if (this.skills.length === 0) return null; + + const availableSkills = this.skills.filter(skill => skill.isAvailable); + if (availableSkills.length === 0) return null; + + this.skill = getRandomArrayElement(availableSkills); + this.skillUsed = true; + const damageDealt = this.skill.effect(opponent); + let message = `(${this.playerClassName}) ${this.playerName} использует (${this.skill.name}) на (${opponent.playerClassName}) ${opponent.playerName}`; + if (damageDealt > 0) { + message += ` и наносит урон ${damageDealt}`; + } + return message; + } + public attack(opponent: Player): string { if (this.isAlivePlayer && !this.isCharmed) { + if (this.skillUsed === true) { + this.skillBuff += 1; + } + if (this.skillBuff === this.skill?.turns) { + this.strength -= this.skill.damage; + } opponent.takeDamage(this.strength); return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; } else if (this.isAlivePlayer && this.isCharmed) { diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts index 8acce1674..1ac97387a 100644 --- a/rpgsaga/saga/src/classes/Vizard.ts +++ b/rpgsaga/saga/src/classes/Vizard.ts @@ -1,6 +1,5 @@ import { Player } from '../abstract/Player'; import { ISkills } from '../skills/ISkills'; -import { Logger } from '../utils/output/Logger'; export class Vizard extends Player { protected className: string = 'Vizard'; @@ -11,6 +10,10 @@ export class Vizard extends Player { return this.countOfSkills; } + public set countOfVizardSkills(value: number) { + this.countOfSkills = value; + } + constructor(health: number, strength: number, name: string) { super(health, strength, name); this.addSkill({ @@ -39,7 +42,7 @@ export class Vizard extends Player { this.countOfSkills += 1; return `Противник не может атаковать ${this.playerName} из-за (Заворожения)`; } else { - super.takeDamage(damage); + return super.takeDamage(damage); } } } diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts index 60beb7903..e1f18968e 100644 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ b/rpgsaga/saga/src/gameplay/Game.ts @@ -60,7 +60,6 @@ export class Game { public async start() { Logger.log('Игра началась!'); - this.players = this.shuffleArray(this.players); await this.tournament(this.players); Logger.log(`Победитель: ${this.players[0].playerName}`); } @@ -80,6 +79,13 @@ export class Game { player2.healthPoints = this.initialHealth[this.players.indexOf(player2)]; player1.strengthPoints = this.initialStrength[this.players.indexOf(player1)]; player2.strengthPoints = this.initialStrength[this.players.indexOf(player2)]; + player1.playerSkillUsed = false; + player2.playerSkillUsed = false; + if (player1 instanceof Vizard && player1.countOfVizardSkills >= 1) { + player1.countOfVizardSkills = 0; + } else if (player2 instanceof Vizard && player2.countOfVizardSkills >= 1) { + player2.countOfVizardSkills = 0; + } } return this.tournament(nextRoundPlayers); @@ -122,14 +128,6 @@ export class Game { return fighters.find(player => player.healthPoints > 0)!; } - private shuffleArray(array: Player[]): Player[] { - for (let i = array.length - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)); - [array[i], array[j]] = [array[j], array[i]]; - } - return array; - } - private delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/rpgsaga/saga/src/skills/ISkills.ts b/rpgsaga/saga/src/skills/ISkills.ts index 66689dcb8..e4cf26d5f 100644 --- a/rpgsaga/saga/src/skills/ISkills.ts +++ b/rpgsaga/saga/src/skills/ISkills.ts @@ -5,5 +5,5 @@ export interface ISkills { damage?: number; turns?: number; isAvailable: boolean; - effect?: (opponent: Player) => number; + effect: (opponent: Player) => number; } diff --git a/rpgsaga/saga/src/utils/input/input.ts b/rpgsaga/saga/src/utils/input/input.ts index 989b0dddb..5f54f6da8 100644 --- a/rpgsaga/saga/src/utils/input/input.ts +++ b/rpgsaga/saga/src/utils/input/input.ts @@ -9,9 +9,9 @@ export function input(): void { }); function askForPlayers() { - rl.question('Введите число игроков (должно быть чётным): ', inputNumber => { + rl.question('Введите число игроков (должно делиться на 4): ', inputNumber => { const number = parseInt(inputNumber); - if (isNaN(number) || number < 1 || number % 2 !== 0) { + if (isNaN(number) || number < 1 || number % 4 !== 0) { console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); askForPlayers(); } else { From 9851e922e681f50b4b1e2eced9fe94392626e699 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Mon, 4 Nov 2024 13:45:01 +0300 Subject: [PATCH 14/27] saga fixes + hard polymorphism --- rpgsaga/saga/src/abstract/Player.ts | 8 +- rpgsaga/saga/src/classes/Archer.ts | 10 ++- .../classes/abstract/Person.ts} | 52 ++++++------ .../classes/workers/Accountant.ts | 40 +++++++++ .../funcAndClasses/classes/workers/Doctor.ts | 40 +++++++++ .../function}/function.ts | 0 rpgsaga/saga/src/index.ts | 13 ++- .../funcAndClassTests/Accountant.spec.ts | 85 +++++++++++++++++++ .../tests/funcAndClassTests/Doctor.spec.ts | 83 ++++++++++++++++++ .../tests/funcAndClassTests/Person.spec.ts | 59 ------------- .../tests/funcAndClassTests/example.spec.ts | 2 +- rpgsaga/saga/tests/sagaTests/Archer.spec.ts | 4 - 12 files changed, 302 insertions(+), 94 deletions(-) rename rpgsaga/saga/src/{funcAndClass/person.ts => funcAndClasses/classes/abstract/Person.ts} (54%) create mode 100644 rpgsaga/saga/src/funcAndClasses/classes/workers/Accountant.ts create mode 100644 rpgsaga/saga/src/funcAndClasses/classes/workers/Doctor.ts rename rpgsaga/saga/src/{funcAndClass => funcAndClasses/function}/function.ts (100%) create mode 100644 rpgsaga/saga/tests/funcAndClassTests/Accountant.spec.ts create mode 100644 rpgsaga/saga/tests/funcAndClassTests/Doctor.spec.ts delete mode 100644 rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts index e78e85d94..9c53b038f 100644 --- a/rpgsaga/saga/src/abstract/Player.ts +++ b/rpgsaga/saga/src/abstract/Player.ts @@ -60,10 +60,14 @@ export abstract class Player { public abstract attack(opponent: Player): string; public useSkill(opponent: Player): string | null { - if (this.skills.length === 0) return null; + if (this.skills.length === 0) { + return null; + } const availableSkills = this.skills.filter(skill => skill.isAvailable); - if (availableSkills.length === 0) return null; + if (availableSkills.length === 0) { + return null; + } const skill = getRandomArrayElement(availableSkills); this.skillUsed = true; diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts index d40758ac8..70d86f83e 100644 --- a/rpgsaga/saga/src/classes/Archer.ts +++ b/rpgsaga/saga/src/classes/Archer.ts @@ -35,10 +35,14 @@ export class Archer extends Player { } public useSkill(opponent: Player): string | null { - if (this.skills.length === 0) return null; + if (this.skills.length === 0) { + return null; + } const availableSkills = this.skills.filter(skill => skill.isAvailable); - if (availableSkills.length === 0) return null; + if (availableSkills.length === 0) { + return null; + } this.skill = getRandomArrayElement(availableSkills); this.skillUsed = true; @@ -50,6 +54,8 @@ export class Archer extends Player { return message; } + //передавать в класс, получаемый урон созданный объект, копирующий параметры скилла и уже его обрабатывать + public attack(opponent: Player): string { if (this.isAlivePlayer && !this.isCharmed) { if (this.skillUsed === true) { diff --git a/rpgsaga/saga/src/funcAndClass/person.ts b/rpgsaga/saga/src/funcAndClasses/classes/abstract/Person.ts similarity index 54% rename from rpgsaga/saga/src/funcAndClass/person.ts rename to rpgsaga/saga/src/funcAndClasses/classes/abstract/Person.ts index 2c81a1074..d7a7ed5b5 100644 --- a/rpgsaga/saga/src/funcAndClass/person.ts +++ b/rpgsaga/saga/src/funcAndClasses/classes/abstract/Person.ts @@ -1,35 +1,23 @@ -export class Person { - private _name: string; - private _sex: string; - private _age: number; +export abstract class Person { + protected _name: string; + protected _sex: string; + protected _age: number; - constructor(personFirstName, personAge, personSex) { - this.name = personFirstName; + constructor(personName, personAge, personSex) { + this.name = personName; this.age = personAge; this.sex = personSex; } - toString(): string { - return `${this._name} ${this._sex}`; - } - - toNumber(): number { - return this._age; - } - - print(): string { - return `first name: ${this._name}\nsex: ${this._sex}\nage: ${this._age}`; - } - public get age(): number { return this._age; } - public set age(n: number) { - if (n < 0 || n > 110) { + public set age(newAge: number) { + if (newAge < 0 || newAge > 110) { throw new Error('Недопустимый возраст'); } else { - this._age = n; + this._age = newAge; } } @@ -37,20 +25,34 @@ export class Person { return this._name; } - public set name(n: string) { - this._name = n; + public set name(newName: string) { + this._name = newName; } public get sex(): string { return this._sex; } - public set sex(n: string) { - const personSex = n.toLowerCase(); + public set sex(newSex: string) { + const personSex = newSex.toLowerCase(); if (personSex === 'male' || personSex === 'female') { this._sex = personSex; } else { throw new Error('Недопустимый гендер'); } } + + toString(): string { + return `${this._name} ${this._sex}`; + } + + toNumber(): number { + return this._age; + } + + print(): string { + return `name: ${this._name}\nsex: ${this._sex}\nage: ${this._age}`; + } + + abstract workRespons(): string; } diff --git a/rpgsaga/saga/src/funcAndClasses/classes/workers/Accountant.ts b/rpgsaga/saga/src/funcAndClasses/classes/workers/Accountant.ts new file mode 100644 index 000000000..0dd3468dd --- /dev/null +++ b/rpgsaga/saga/src/funcAndClasses/classes/workers/Accountant.ts @@ -0,0 +1,40 @@ +import { Person } from '../abstract/Person'; + +export class Accountant extends Person { + private _work: string; + private _salary: number; + + constructor(personName, personAge, personSex, personWork, personSalary) { + super(personName, personAge, personSex); + this.work = personWork; + this.salary = personSalary; + } + + public get work(): string { + return this._work; + } + + public set work(workName: string) { + this._work = workName; + } + + public get salary(): number { + return this._salary; + } + + public set salary(salaryValue: number) { + if (salaryValue < 0) { + throw new Error('Недопустимая зарплата'); + } else { + this._salary = salaryValue; + } + } + + public workRespons(): string { + return `${this.name} does paperwork`; + } + + toString(): string { + return `${this._name} ${this._sex} ${this._work}`; + } +} diff --git a/rpgsaga/saga/src/funcAndClasses/classes/workers/Doctor.ts b/rpgsaga/saga/src/funcAndClasses/classes/workers/Doctor.ts new file mode 100644 index 000000000..2a1918ca4 --- /dev/null +++ b/rpgsaga/saga/src/funcAndClasses/classes/workers/Doctor.ts @@ -0,0 +1,40 @@ +import { Person } from '../abstract/Person'; + +export class Doctor extends Person { + private _work: string; + private _salary: number; + + constructor(personName, personAge, personSex, personWork, personSalary) { + super(personName, personAge, personSex); + this.work = personWork; + this.salary = personSalary; + } + + public get work(): string { + return this._work; + } + + public set work(workName: string) { + this._work = workName; + } + + public get salary(): number { + return this._salary; + } + + public set salary(salaryValue: number) { + if (salaryValue < 0) { + throw new Error('Недопустимая зарплата'); + } else { + this._salary = salaryValue; + } + } + + public workRespons(): string { + return `${this.name} heals people`; + } + + toString(): string { + return `${this._name} ${this._sex} ${this._work}`; + } +} diff --git a/rpgsaga/saga/src/funcAndClass/function.ts b/rpgsaga/saga/src/funcAndClasses/function/function.ts similarity index 100% rename from rpgsaga/saga/src/funcAndClass/function.ts rename to rpgsaga/saga/src/funcAndClasses/function/function.ts diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index dd786bb01..aea9960f8 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,8 +1,19 @@ import { input } from './utils/input/input'; -import { taskA, taskB, output } from './funcAndClass/function'; +import { taskA, taskB, output } from './funcAndClasses/function/function'; +import { Person } from './funcAndClasses/classes/abstract/Person'; +import { Accountant } from './funcAndClasses/classes/workers/Accountant'; +import { Doctor } from './funcAndClasses/classes/workers/Doctor'; console.log(output('A', taskA(1.6, 1.2, 3.7, 0.5))); console.log(output('B', taskB(1.6, [1.28, 1.36, 2.47, 3.68, 4.56]))); +const accountant: Person = new Accountant('John', 34, 'Male', 'Sberbank', 40000); +const doctor: Person = new Doctor('Mary', 25, 'Female', 'LidEnf', 25000); + +const arrOfPersons: Person[] = [accountant, doctor]; +for (const pers of arrOfPersons) { + console.log(pers.workRespons()); +} + input(); diff --git a/rpgsaga/saga/tests/funcAndClassTests/Accountant.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/Accountant.spec.ts new file mode 100644 index 000000000..d8478da2d --- /dev/null +++ b/rpgsaga/saga/tests/funcAndClassTests/Accountant.spec.ts @@ -0,0 +1,85 @@ +import { Accountant } from '../../src/funcAndClasses/classes/workers/Accountant'; + +describe('Person class methods tests', () => { + it('Constructor test', () => { + let newAccountant = new Accountant('TestPerson', 20, 'Male', 'Sberbank', 40000); + expect(newAccountant.age).toEqual(20); + expect(newAccountant.name).toBe('TestPerson'); + expect(newAccountant.sex).toBe('male'); + expect(newAccountant.work).toBe('Sberbank'); + expect(newAccountant.salary).toEqual(40000); + }); + describe('Get methods tests', () => { + let newAccountant = new Accountant('TestPerson', 20, 'Male', 'Sberbank', 40000); + it('Age get test', () => { + expect(newAccountant.age).toEqual(20); + }); + it('Name get test', () => { + expect(newAccountant.name).toBe('TestPerson'); + }); + it('Sex get test', () => { + expect(newAccountant.sex).toBe('male'); + }); + it('Work get test', () => { + expect(newAccountant.work).toBe('Sberbank'); + }); + it('Salary get test', () => { + expect(newAccountant.salary).toEqual(40000); + }); + }); + describe('Set methods tests', () => { + let newAccountant = new Accountant('TestPerson', 20, 'Male', 'Sberbank', 40000); + it('Age basic test', () => { + newAccountant.age = 35; + expect(newAccountant.age).toEqual(35); + }); + it('Age negative test', () => { + expect(() => { + newAccountant.age = -1; + }).toThrow(Error('Недопустимый возраст')); + }); + it('Name test', () => { + newAccountant.name = 'John'; + expect(newAccountant.name).toBe('John'); + }); + it('Sex basic test', () => { + newAccountant.sex = 'Female'; + expect(newAccountant.sex).toBe('female'); + }); + it('Sex uncorrect test', () => { + expect(() => { + newAccountant.sex = 'dog'; + }).toThrow(Error('Недопустимый гендер')); + }); + it('Salary basic test', () => { + newAccountant.salary = 35000; + expect(newAccountant.salary).toEqual(35000); + }); + it('Salary uncorrect test', () => { + expect(() => { + newAccountant.salary = -1; + }).toThrow(Error('Недопустимая зарплата')); + }); + it('Work test', () => { + newAccountant.work = 'AlphaBank'; + expect(newAccountant.work).toBe('AlphaBank'); + }); + }); + describe('Other methods tests', () => { + let newAccountant = new Accountant('John', 20, 'Male', 'Sberbank', 40000); + it('Should return name, sex and work', () => { + expect(newAccountant.toString()).toEqual(`${newAccountant.name} ${newAccountant.sex} ${newAccountant.work}`); + }); + it('Should return age as number', () => { + expect(newAccountant.toNumber()).toEqual(newAccountant.age); + }); + it('Should return all properties', () => { + expect(newAccountant.print()).toEqual( + `name: ${newAccountant.name}\nsex: ${newAccountant.sex}\nage: ${newAccountant.age}`, + ); + }); + it('Should return work', () => { + expect(newAccountant.workRespons()).toEqual(`${newAccountant.name} does paperwork`); + }); + }); +}); diff --git a/rpgsaga/saga/tests/funcAndClassTests/Doctor.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/Doctor.spec.ts new file mode 100644 index 000000000..e828b42e5 --- /dev/null +++ b/rpgsaga/saga/tests/funcAndClassTests/Doctor.spec.ts @@ -0,0 +1,83 @@ +import { Doctor } from '../../src/funcAndClasses/classes/workers/Doctor'; + +describe('Person class methods tests', () => { + it('Constructor test', () => { + let newDoctor = new Doctor('Mary', 25, 'Female', 'LidEnf', 25000); + expect(newDoctor.age).toEqual(25); + expect(newDoctor.name).toBe('Mary'); + expect(newDoctor.sex).toBe('female'); + expect(newDoctor.work).toBe('LidEnf'); + expect(newDoctor.salary).toEqual(25000); + }); + describe('Get methods tests', () => { + let newDoctor = new Doctor('Mary', 25, 'Female', 'LidEnf', 25000); + it('Age get test', () => { + expect(newDoctor.age).toEqual(25); + }); + it('Name get test', () => { + expect(newDoctor.name).toBe('Mary'); + }); + it('Sex get test', () => { + expect(newDoctor.sex).toBe('female'); + }); + it('Work get test', () => { + expect(newDoctor.work).toBe('LidEnf'); + }); + it('Salary get test', () => { + expect(newDoctor.salary).toEqual(25000); + }); + }); + describe('Set methods tests', () => { + let newDoctor = new Doctor('Mary', 25, 'Female', 'LidEnf', 25000); + it('Age basic test', () => { + newDoctor.age = 35; + expect(newDoctor.age).toEqual(35); + }); + it('Age negative test', () => { + expect(() => { + newDoctor.age = -1; + }).toThrow(Error('Недопустимый возраст')); + }); + it('Name test', () => { + newDoctor.name = 'Jess'; + expect(newDoctor.name).toBe('Jess'); + }); + it('Sex basic test', () => { + newDoctor.sex = 'Male'; + expect(newDoctor.sex).toBe('male'); + }); + it('Sex uncorrect test', () => { + expect(() => { + newDoctor.sex = 'dog'; + }).toThrow(Error('Недопустимый гендер')); + }); + it('Salary basic test', () => { + newDoctor.salary = 35000; + expect(newDoctor.salary).toEqual(35000); + }); + it('Salary uncorrect test', () => { + expect(() => { + newDoctor.salary = -1; + }).toThrow(Error('Недопустимая зарплата')); + }); + it('Work test', () => { + newDoctor.work = 'Kranex'; + expect(newDoctor.work).toBe('Kranex'); + }); + }); + describe('Other methods tests', () => { + let newDoctor = new Doctor('Mary', 25, 'Female', 'LidEnf', 25000); + it('Should return name, sex and work', () => { + expect(newDoctor.toString()).toEqual(`${newDoctor.name} ${newDoctor.sex} ${newDoctor.work}`); + }); + it('Should return age as number', () => { + expect(newDoctor.toNumber()).toEqual(newDoctor.age); + }); + it('Should return all properties', () => { + expect(newDoctor.print()).toEqual(`name: ${newDoctor.name}\nsex: ${newDoctor.sex}\nage: ${newDoctor.age}`); + }); + it('Should return work', () => { + expect(newDoctor.workRespons()).toEqual(`${newDoctor.name} heals people`); + }); + }); +}); diff --git a/rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts deleted file mode 100644 index 54c4dd0e7..000000000 --- a/rpgsaga/saga/tests/funcAndClassTests/Person.spec.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Person } from '../../src/funcAndClass/person'; - -describe('Person class methods tests', () => { - it('Constructor test', () => { - let newPerson = new Person('TestPerson', 20, 'Male'); - expect(newPerson.age).toEqual(20); - expect(newPerson.name).toBe('TestPerson'); - expect(newPerson.sex).toBe('male'); - }); - describe('Get methods tests', () => { - let newPerson = new Person('TestPerson', 20, 'Male'); - it('Age get test', () => { - expect(newPerson.age).toEqual(20); - }); - it('Name get test', () => { - expect(newPerson.name).toBe('TestPerson'); - }); - it('Sex get test', () => { - expect(newPerson.sex).toBe('male'); - }); - }); - describe('Set methods tests', () => { - let newPerson = new Person('TestPerson', 20, 'Male'); - it('Age basic test', () => { - newPerson.age = 35; - expect(newPerson.age).toEqual(35); - }); - it('Age negative test', () => { - expect(() => { - newPerson.age = -1; - }).toThrow(Error('Недопустимый возраст')); - }); - it('Name test', () => { - newPerson.name = 'John'; - expect(newPerson.name).toBe('John'); - }); - it('Sex basic test', () => { - newPerson.sex = 'Female'; - expect(newPerson.sex).toBe('female'); - }); - it('Sex uncorrect test', () => { - expect(() => { - newPerson.sex = 'dog'; - }).toThrow(Error('Недопустимый гендер')); - }); - }); - describe('Other methods tests', () => { - let newPerson = new Person('John', 20, 'Male'); - it('Should return name and sex', () => { - expect(newPerson.toString()).toEqual(`${newPerson.name} ${newPerson.sex}`); - }); - it('Should return age as number', () => { - expect(newPerson.toNumber()).toEqual(newPerson.age); - }); - it('Should return all properties', () => { - expect(newPerson.print()).toEqual(`first name: ${newPerson.name}\nsex: ${newPerson.sex}\nage: ${newPerson.age}`); - }); - }); -}); diff --git a/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts b/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts index 1942bf265..22d235dfa 100644 --- a/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts +++ b/rpgsaga/saga/tests/funcAndClassTests/example.spec.ts @@ -1,4 +1,4 @@ -import { taskA, taskB } from '../../src/funcAndClass/function'; +import { taskA, taskB } from '../../src/funcAndClasses/function/function'; describe('Tests taskA', () => { it('should return 6 values', () => { diff --git a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts index 64c67af65..3a6c4f112 100644 --- a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -26,10 +26,6 @@ describe('Archer class methods tests', () => { newArcher.useSkill(opponent); expect(newArcher.playerSkillUsed).toEqual(true); }); - it('Should return health after an attack using a skill', () => { - newArcher.attack(opponent); - expect(opponent.healthPoints).toEqual(86 - (newArcher.strengthPoints + 2)); - }); }); describe('Archer methods tests', () => { let newArcher = new Archer(75, 25, 'Ibragim'); From ca8342e2f7a87b49f7b1e79cafcc416fbb53d1ce Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Thu, 21 Nov 2024 13:28:29 +0300 Subject: [PATCH 15/27] fix eslint --- rpgsaga/saga/eslint.config.mjs | 302 +++++++++++++++++++-------------- 1 file changed, 173 insertions(+), 129 deletions(-) diff --git a/rpgsaga/saga/eslint.config.mjs b/rpgsaga/saga/eslint.config.mjs index 5e7a9c75c..4fc33fcc9 100644 --- a/rpgsaga/saga/eslint.config.mjs +++ b/rpgsaga/saga/eslint.config.mjs @@ -1,157 +1,201 @@ -import { fixupConfigRules, fixupPluginRules } from "@eslint/compat"; -import typescriptEslint from "@typescript-eslint/eslint-plugin"; -import prettier from "eslint-plugin-prettier"; -import _import from "eslint-plugin-import"; -import globals from "globals"; -import tsParser from "@typescript-eslint/parser"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import js from "@eslint/js"; -import { FlatCompat } from "@eslint/eslintrc"; +import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; +import typescriptEslint from '@typescript-eslint/eslint-plugin'; +import prettier from 'eslint-plugin-prettier'; +import _import from 'eslint-plugin-import'; +import globals from 'globals'; +import tsParser from '@typescript-eslint/parser'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import js from '@eslint/js'; +import { FlatCompat } from '@eslint/eslintrc'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ - baseDirectory: __dirname, - recommendedConfig: js.configs.recommended, - allConfig: js.configs.all + baseDirectory: __dirname, + recommendedConfig: js.configs.recommended, + allConfig: js.configs.all, }); -export default [{ - ignores: ["**/.eslintrc.js", "**/react-app-env.d.ts"], -}, ...fixupConfigRules(compat.extends( - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/errors", - "plugin:import/warnings", - "plugin:import/typescript", - "plugin:prettier/recommended", -)), { +export default [ + { + ignores: ['**/.eslintrc.js', '**/react-app-env.d.ts'], + }, + ...fixupConfigRules( + compat.extends( + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + 'plugin:import/typescript', + 'plugin:prettier/recommended', + ), + ), + { plugins: { - "@typescript-eslint": fixupPluginRules(typescriptEslint), - prettier: fixupPluginRules(prettier), - import: fixupPluginRules(_import), + '@typescript-eslint': fixupPluginRules(typescriptEslint), + prettier: fixupPluginRules(prettier), + import: fixupPluginRules(_import), }, languageOptions: { - globals: { - ...globals.browser, - }, + globals: { + ...globals.browser, + }, - parser: tsParser, - ecmaVersion: 12, - sourceType: "module", + parser: tsParser, + ecmaVersion: 12, + sourceType: 'module', - parserOptions: { - ecmaFeatures: { - jsx: true, - }, + parserOptions: { + ecmaFeatures: { + jsx: true, }, + }, }, settings: { - "import/resolver": { - node: { - extensions: [".js", ".jsx", ".ts", ".tsx"], - moduleDirectory: ["node_modules", "."], - }, + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + moduleDirectory: ['node_modules', '.'], }, + }, }, rules: { - "no-param-reassign": ["error"], + 'no-param-reassign': ['error'], - "prettier/prettier": ["error", { - endOfLine: "auto", - }], - - "linebreak-style": 0, + 'prettier/prettier': [ + 'error', + { + endOfLine: 'auto', + }, + ], - "arrow-body-style": ["error", "as-needed", { - requireReturnForObjectLiteral: false, - }], + 'linebreak-style': 0, - curly: ["error", "all"], - "no-implicit-coercion": ["error"], - "spaced-comment": ["error", "always"], - eqeqeq: ["error", "always"], - "prefer-template": "error", - "no-useless-concat": "error", + 'arrow-body-style': [ + 'error', + 'as-needed', + { + requireReturnForObjectLiteral: false, + }, + ], + + curly: ['error', 'all'], + 'no-implicit-coercion': ['error'], + 'spaced-comment': ['error', 'always'], + eqeqeq: ['error', 'always'], + 'prefer-template': 'error', + 'no-useless-concat': 'error', + + 'prefer-destructuring': [ + 'error', + { + VariableDeclarator: { + array: false, + object: true, + }, + + AssignmentExpression: { + array: false, + object: true, + }, + }, + { + enforceForRenamedProperties: false, + }, + ], + + 'import/extensions': [ + 'error', + 'ignorePackages', + { + ts: 'never', + tsx: 'never', + }, + ], - "prefer-destructuring": ["error", { - VariableDeclarator: { - array: false, - object: true, + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + }, + ], + + 'import/order': [ + 'error', + { + 'newlines-between': 'always', + groups: ['builtin', 'external', 'parent', 'sibling', 'index'], + + pathGroups: [ + { + pattern: 'src/**', + group: 'parent', + position: 'after', }, + ], + }, + ], + + 'import/newline-after-import': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/no-empty-object-type': ['error'], + '@typescript-eslint/no-unsafe-function-type': ['error'], + '@typescript-eslint/no-wrapper-object-types': ['error'], + '@typescript-eslint/no-shadow': ['error'], + + '@typescript-eslint/naming-convention': [ + 'error', + { + selector: 'default', + format: ['camelCase'], + }, + { + selector: ['memberLike'], + modifiers: ['private'], + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + { + selector: ['memberLike'], + modifiers: ['protected'], + format: ['camelCase'], + leadingUnderscore: 'allow', + }, + { + selector: ['enumMember', 'variable'], + format: ['camelCase', 'PascalCase', 'UPPER_CASE'], + }, + { + selector: 'parameter', + format: ['camelCase'], + leadingUnderscore: 'allow', + modifiers: ['unused'], + }, + { + selector: 'objectLiteralProperty', - AssignmentExpression: { - array: false, - object: true, - }, - }, { - enforceForRenamedProperties: false, - }], - - "import/extensions": ["error", "ignorePackages", { - ts: "never", - tsx: "never", - }], - - "import/no-extraneous-dependencies": ["error", { - devDependencies: true, - }], - - "import/order": ["error", { - "newlines-between": "always", - groups: ["builtin", "external", "parent", "sibling", "index"], - - pathGroups: [{ - pattern: "src/**", - group: "parent", - position: "after", - }], - }], - - "import/newline-after-import": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/no-empty-object-type": ["error"], - "@typescript-eslint/no-unsafe-function-type": ["error"], - "@typescript-eslint/no-wrapper-object-types": ["error"], - "@typescript-eslint/no-shadow": ["error"], - - "@typescript-eslint/naming-convention": ["error", { - selector: "default", - format: ["camelCase"], - }, { - selector: ["memberLike"], - modifiers: ["private"], - format: ["camelCase"], - leadingUnderscore: "allow", - }, { - selector: ["enumMember", "variable"], - format: ["camelCase", "PascalCase", "UPPER_CASE"], - }, { - selector: "parameter", - format: ["camelCase"], - leadingUnderscore: "allow", - modifiers: ["unused"], - }, { - selector: "objectLiteralProperty", - - filter: { - regex: "^[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$", - match: true, - }, + filter: { + regex: '^[a-z]([a-z0-9-]+)?(__([a-z0-9]+-?)+)?(--([a-z0-9]+-?)+){0,2}$', + match: true, + }, - format: null, - }, { - selector: "typeLike", - format: ["PascalCase"], - }, { - selector: "typeProperty", - format: ["snake_case", "camelCase"], - }], + format: null, + }, + { + selector: 'typeLike', + format: ['PascalCase'], + }, + { + selector: 'typeProperty', + format: ['snake_case', 'camelCase'], + }, + ], }, -}]; \ No newline at end of file + }, +]; From 73e17d149694377f556d36cef617d6d9b3048273 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 17:17:21 +0300 Subject: [PATCH 16/27] saga + tests --- rpgsaga/saga/src/abstract/Player.ts | 93 --------- rpgsaga/saga/src/classes/Archer.ts | 74 ------- rpgsaga/saga/src/classes/Knight.ts | 30 --- rpgsaga/saga/src/classes/Vizard.ts | 48 ----- rpgsaga/saga/src/game/abstract/Player.ts | 183 ++++++++++++++++++ rpgsaga/saga/src/game/classes/Archer.ts | 17 ++ rpgsaga/saga/src/game/classes/Knight.ts | 29 +++ rpgsaga/saga/src/game/classes/Wizard.ts | 17 ++ rpgsaga/saga/src/game/classes/index.ts | 3 + .../fabrics/playersFabrics/ArcherFabric.ts | 33 ++++ .../fabrics/playersFabrics/KnightFabric.ts | 33 ++++ .../fabrics/playersFabrics/PlayerFabric.ts | 74 +++++++ .../fabrics/playersFabrics/WizardFabric.ts | 33 ++++ .../src/game/fabrics/playersFabrics/index.ts | 4 + .../game/fabrics/skillFabric/SkillFabric.ts | 97 ++++++++++ .../fabrics/weaponsFabric/WeaponFabric.ts | 78 ++++++++ rpgsaga/saga/src/game/gameplay/Game.ts | 97 ++++++++++ rpgsaga/saga/src/game/skills/ISkill.ts | 15 ++ .../src/game/utils/input/createCharacter.ts | 118 +++++++++++ .../saga/src/game/utils/input/createGame.ts | 39 ++++ rpgsaga/saga/src/game/utils/output/Logger.ts | 56 ++++++ .../src/game/utils/question/readAnswer.ts | 15 ++ .../randomization/getRandomArrayElement.ts | 2 +- .../utils/randomization/getRandomNumber.ts | 13 ++ .../src/game/utils/randomization/index.ts | 2 + rpgsaga/saga/src/game/weapon/IWeapon.ts | 5 + rpgsaga/saga/src/gameplay/Game.ts | 134 ------------- rpgsaga/saga/src/index.ts | 4 +- rpgsaga/saga/src/skills/ISkills.ts | 9 - rpgsaga/saga/src/utils/input/input.ts | 26 --- rpgsaga/saga/src/utils/output/Logger.ts | 7 - .../utils/randomization/getRandomNumber.ts | 13 -- rpgsaga/saga/tests/sagaTests/Archer.spec.ts | 144 +++++++++++--- .../saga/tests/sagaTests/ArcherFabric.spec.ts | 73 +++++++ rpgsaga/saga/tests/sagaTests/Game.spec.ts | 118 ++++++++--- rpgsaga/saga/tests/sagaTests/ISkill.spec.ts | 89 +++++++++ rpgsaga/saga/tests/sagaTests/Knight.spec.ts | 157 ++++++++++++--- .../saga/tests/sagaTests/KnightFabric.spec.ts | 71 +++++++ rpgsaga/saga/tests/sagaTests/Logger.spec.ts | 7 - .../saga/tests/sagaTests/PlayerFabric.spec.ts | 122 ++++++++++++ .../saga/tests/sagaTests/SkillFabric.spec.ts | 76 ++++++++ rpgsaga/saga/tests/sagaTests/Vizard.spec.ts | 49 ----- .../saga/tests/sagaTests/WeaponFabric.spec.ts | 70 +++++++ rpgsaga/saga/tests/sagaTests/Wizard.spec.ts | 142 ++++++++++++++ .../saga/tests/sagaTests/WizardFabric.spec.ts | 71 +++++++ 45 files changed, 2021 insertions(+), 569 deletions(-) delete mode 100644 rpgsaga/saga/src/abstract/Player.ts delete mode 100644 rpgsaga/saga/src/classes/Archer.ts delete mode 100644 rpgsaga/saga/src/classes/Knight.ts delete mode 100644 rpgsaga/saga/src/classes/Vizard.ts create mode 100644 rpgsaga/saga/src/game/abstract/Player.ts create mode 100644 rpgsaga/saga/src/game/classes/Archer.ts create mode 100644 rpgsaga/saga/src/game/classes/Knight.ts create mode 100644 rpgsaga/saga/src/game/classes/Wizard.ts create mode 100644 rpgsaga/saga/src/game/classes/index.ts create mode 100644 rpgsaga/saga/src/game/fabrics/playersFabrics/ArcherFabric.ts create mode 100644 rpgsaga/saga/src/game/fabrics/playersFabrics/KnightFabric.ts create mode 100644 rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts create mode 100644 rpgsaga/saga/src/game/fabrics/playersFabrics/WizardFabric.ts create mode 100644 rpgsaga/saga/src/game/fabrics/playersFabrics/index.ts create mode 100644 rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts create mode 100644 rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts create mode 100644 rpgsaga/saga/src/game/gameplay/Game.ts create mode 100644 rpgsaga/saga/src/game/skills/ISkill.ts create mode 100644 rpgsaga/saga/src/game/utils/input/createCharacter.ts create mode 100644 rpgsaga/saga/src/game/utils/input/createGame.ts create mode 100644 rpgsaga/saga/src/game/utils/output/Logger.ts create mode 100644 rpgsaga/saga/src/game/utils/question/readAnswer.ts rename rpgsaga/saga/src/{ => game}/utils/randomization/getRandomArrayElement.ts (68%) create mode 100644 rpgsaga/saga/src/game/utils/randomization/getRandomNumber.ts create mode 100644 rpgsaga/saga/src/game/utils/randomization/index.ts create mode 100644 rpgsaga/saga/src/game/weapon/IWeapon.ts delete mode 100644 rpgsaga/saga/src/gameplay/Game.ts delete mode 100644 rpgsaga/saga/src/skills/ISkills.ts delete mode 100644 rpgsaga/saga/src/utils/input/input.ts delete mode 100644 rpgsaga/saga/src/utils/output/Logger.ts delete mode 100644 rpgsaga/saga/src/utils/randomization/getRandomNumber.ts create mode 100644 rpgsaga/saga/tests/sagaTests/ArcherFabric.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/ISkill.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/KnightFabric.spec.ts delete mode 100644 rpgsaga/saga/tests/sagaTests/Logger.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/PlayerFabric.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts delete mode 100644 rpgsaga/saga/tests/sagaTests/Vizard.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/Wizard.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/WizardFabric.spec.ts diff --git a/rpgsaga/saga/src/abstract/Player.ts b/rpgsaga/saga/src/abstract/Player.ts deleted file mode 100644 index 9c53b038f..000000000 --- a/rpgsaga/saga/src/abstract/Player.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { ISkills } from '../skills/ISkills'; -import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; - -export abstract class Player { - protected health: number; - protected strength: number; - protected name: string; - protected className: string; - protected isAlive: boolean = true; - protected skillUsed: boolean = false; - protected skills: ISkills[] = []; - protected isCharmed: boolean = false; - - constructor(gamerHealth: number, gamerStrength: number, gamerName: string) { - this.health = gamerHealth; - this.strength = gamerStrength; - this.name = gamerName; - } - - public get healthPoints(): number { - return this.health; - } - - public set healthPoints(newHealthPoints: number) { - this.health = newHealthPoints; - } - - public get strengthPoints(): number { - return this.strength; - } - - public set strengthPoints(newStrengthPoints: number) { - this.strength = newStrengthPoints; - } - - public get playerName(): string { - return this.name; - } - - public get playerClassName(): string { - return this.className; - } - - public get isAlivePlayer(): boolean { - return this.isAlive; - } - - public get playerSkillUsed(): boolean { - return this.skillUsed; - } - - public set playerSkillUsed(value: boolean) { - this.skillUsed = value; - } - - public addSkill(skill: ISkills): void { - this.skills.push(skill); - } - - public abstract attack(opponent: Player): string; - - public useSkill(opponent: Player): string | null { - if (this.skills.length === 0) { - return null; - } - - const availableSkills = this.skills.filter(skill => skill.isAvailable); - if (availableSkills.length === 0) { - return null; - } - - const skill = getRandomArrayElement(availableSkills); - this.skillUsed = true; - const damageDealt = skill.effect(opponent); - let message = `(${this.playerClassName}) ${this.playerName} использует (${skill.name}) на (${opponent.playerClassName}) ${opponent.playerName}`; - if (damageDealt > 0) { - message += ` и наносит урон ${damageDealt}`; - } - return message; - } - - public takeDamage(damage: number): string { - this.health -= damage; - if (this.health <= 0) { - this.isAlive = false; - return `(${this.playerClassName}) ${this.playerName} погибает`; - } - } - - public gettingCharmed(value: boolean): void { - this.isCharmed = value; - } -} diff --git a/rpgsaga/saga/src/classes/Archer.ts b/rpgsaga/saga/src/classes/Archer.ts deleted file mode 100644 index 915a203db..000000000 --- a/rpgsaga/saga/src/classes/Archer.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Player } from '../abstract/Player'; -import { ISkills } from '../skills/ISkills'; -import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; - -export class Archer extends Player { - protected className: string = 'Archer'; - protected skill: ISkills; - protected skillBuff: number = 0; - - constructor(health: number, strength: number, name: string) { - super(health, strength, name); - this.addSkill({ - name: 'Огненные стрелы', - damage: 2, - isAvailable: true, - effect: opponent => { - if (opponent.playerClassName === 'Knight') { - return 0; - } else { - this.strength += 2; - return 0; - } - }, - }); - this.addSkill({ - name: 'Ледяные стрелы', - damage: 3, - turns: 3, - isAvailable: true, - effect: () => { - this.strength += 3; - return 0; - }, - }); - } - - public useSkill(opponent: Player): string | null { - if (this.skills.length === 0) { - return null; - } - - const availableSkills = this.skills.filter(skill => skill.isAvailable); - if (availableSkills.length === 0) { - return null; - } - - this.skill = getRandomArrayElement(availableSkills); - this.skillUsed = true; - const damageDealt = this.skill.effect(opponent); - let message = `(${this.playerClassName}) ${this.playerName} использует (${this.skill.name}) на (${opponent.playerClassName}) ${opponent.playerName}`; - if (damageDealt > 0) { - message += ` и наносит урон ${damageDealt}`; - } - return message; - } - - // передавать в класс, получаемый урон созданный объект, копирующий параметры скилла и уже его обрабатывать - - public attack(opponent: Player): string { - if (this.isAlivePlayer && !this.isCharmed) { - if (this.skillUsed === true) { - this.skillBuff += 1; - } - if (this.skillBuff === this.skill?.turns) { - this.strength -= this.skill.damage; - } - opponent.takeDamage(this.strength); - return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; - } else if (this.isAlivePlayer && this.isCharmed) { - this.gettingCharmed(false); - return opponent.takeDamage(this.strength); - } - } -} diff --git a/rpgsaga/saga/src/classes/Knight.ts b/rpgsaga/saga/src/classes/Knight.ts deleted file mode 100644 index ce8855383..000000000 --- a/rpgsaga/saga/src/classes/Knight.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Player } from '../abstract/Player'; -import { ISkills } from '../skills/ISkills'; - -export class Knight extends Player { - protected className: string = 'Knight'; - protected skill: ISkills; - - constructor(health: number, strength: number, name: string) { - super(health, strength, name); - this.addSkill({ - name: 'Удар возмездия', - isAvailable: true, - effect: opponent => { - const skillDamage = this.strength * 1.3; - opponent.takeDamage(skillDamage); - return skillDamage; - }, - }); - } - - public attack(opponent: Player): string { - if (this.isAlivePlayer && !this.isCharmed) { - opponent.takeDamage(this.strength); - return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; - } else if (this.isAlivePlayer && this.isCharmed) { - this.gettingCharmed(false); - return opponent.takeDamage(this.strength); - } - } -} diff --git a/rpgsaga/saga/src/classes/Vizard.ts b/rpgsaga/saga/src/classes/Vizard.ts deleted file mode 100644 index 1ac97387a..000000000 --- a/rpgsaga/saga/src/classes/Vizard.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Player } from '../abstract/Player'; -import { ISkills } from '../skills/ISkills'; - -export class Vizard extends Player { - protected className: string = 'Vizard'; - protected countOfSkills: number = 0; - protected skill: ISkills; - - public get countOfVizardSkills() { - return this.countOfSkills; - } - - public set countOfVizardSkills(value: number) { - this.countOfSkills = value; - } - - constructor(health: number, strength: number, name: string) { - super(health, strength, name); - this.addSkill({ - name: 'Заворожение', - isAvailable: true, - effect: opponent => { - opponent.gettingCharmed(true); - return 0; - }, - }); - } - - public attack(opponent: Player): string { - if (this.isAlivePlayer && !this.isCharmed) { - opponent.takeDamage(this.strength); - return `(${this.playerClassName}) ${this.playerName} наносит урон ${this.strength} противнику (${opponent.playerClassName}) ${opponent.playerName}`; - } else if (this.isAlivePlayer && this.isCharmed) { - this.gettingCharmed(false); - return opponent.takeDamage(this.strength); - } - } - - public takeDamage(damage: number): string { - if (this.skillUsed) { - this.skillUsed = false; - this.countOfSkills += 1; - return `Противник не может атаковать ${this.playerName} из-за (Заворожения)`; - } else { - return super.takeDamage(damage); - } - } -} diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts new file mode 100644 index 000000000..f425c389d --- /dev/null +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -0,0 +1,183 @@ +import { ISkill } from '../skills/ISkill'; +import { getRandomArrayElement } from '../utils/randomization'; +import { IWeapon } from '../weapon/IWeapon'; + +export abstract class Player { + protected _name: string; + protected _className?: string; + protected _initialHealth: number; + protected _health: number; + protected _initialStrength: number; + protected _strength: number; + protected _skills: ISkill[]; + protected _currentSkill?: ISkill; + protected skillBuff: number = 0; + protected _isSkillUsed: boolean = false; + protected _isAlive: boolean = true; + protected _countOfSkipingTurns: number = 0; + protected _weapon: IWeapon; + + constructor( + playerHealth: number, + playerStrength: number, + playerName: string, + playerWeapon: IWeapon, + playerSkills: ISkill[], + ) { + this._initialHealth = playerHealth; + this._health = this.initialHealth; + this._initialStrength = playerStrength; + this._strength = this.initialStrength; + this._name = playerName; + this._weapon = playerWeapon; + this._skills = playerSkills; + } + + public get className(): string | undefined { + return this._className; + } + + public get name(): string { + return this._name; + } + + public get isAlive(): boolean { + return this._isAlive; + } + + public get isSkillUsed(): boolean { + return this._isSkillUsed; + } + + public get health(): number { + return this._health; + } + + public get strength(): number { + return this._strength; + } + + public get initialHealth(): number { + return this._initialHealth; + } + + public get initialStrength(): number { + return this._initialStrength; + } + + public get countOfSkipingTurns(): number { + return this._countOfSkipingTurns; + } + + public get weapon(): IWeapon { + return this._weapon; + } + + public get currentSkill(): ISkill | undefined { + return this._currentSkill; + } + + public get skills(): ISkill[] { + return this._skills; + } + + public choseSkill(): void { + this._currentSkill = getRandomArrayElement(this.skills); + } + + public useSkill(opponent: Player, skillName: string | null = null): void { + if (this.skills.length === 0) { + return; + } + + if (skillName !== null) { + this.skills.forEach(skill => { + if (skill.name === skillName.toLowerCase()) { + this._currentSkill = skill; + return; + } + }); + } + + if (this._currentSkill !== undefined && this._currentSkill.usageCount > 0) { + this._currentSkill.effect!(this, opponent); + this._currentSkill.usageCount--; + this.skills.forEach(skill => { + if (skill.name === this._currentSkill!.name) { + skill.usageCount--; + } + }); + this._isSkillUsed = true; + } + } + + public attack(opponent: Player): void { + if (this.countOfSkipingTurns > 0) { + this._countOfSkipingTurns--; + return; + } + + if (this._currentSkill) { + const skillIndex = this._skills.findIndex(skill => skill.name === this._currentSkill!.name); + + if (skillIndex !== -1) { + this._skills[skillIndex].isUsed = true; + this._updateSkills(); + } + + opponent.takeDamage(this._strength + this._weapon.damage, this, this._currentSkill); + } else { + opponent.takeDamage(this._strength + this._weapon.damage, this); + } + } + + protected _updateSkills(): void { + for (const skill of this._skills) { + if (skill.isUsed) { + if (skill.turns! <= 0) { + if (skill.buff) { + this._strength -= skill.buff.strength; + } + skill.isUsed = false; + skill.turns = skill.initialTurns; + } + skill.turns--; + } + } + } + + public damageUp(buff: number) { + this._strength += buff; + } + + public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { + this._health -= damage; + if (this._health <= 0) { + this._health = 0; + this._isAlive = false; + } + } + + public heal(amount: number) { + if (this._health + amount > this.initialHealth) { + this._health = this.initialHealth; + } else { + this._health = this._health + amount; + } + } + + public reset(): void { + this._health = this.initialHealth; + this._strength = this.initialStrength; + this._isSkillUsed = false; + this._skills.forEach(skill => { + skill.usageCount = skill.initialSkillUsage; + skill.isUsed = false; + skill.turns = skill.initialTurns; + }); + } + + public skipTurns(value: number): void { + this._countOfSkipingTurns = value; + } +} diff --git a/rpgsaga/saga/src/game/classes/Archer.ts b/rpgsaga/saga/src/game/classes/Archer.ts new file mode 100644 index 000000000..7ad6c17b1 --- /dev/null +++ b/rpgsaga/saga/src/game/classes/Archer.ts @@ -0,0 +1,17 @@ +import { Player } from '../abstract/Player'; +import { ISkill } from '../skills/ISkill'; +import { IWeapon } from '../weapon/IWeapon'; + +export class Archer extends Player { + public _className: string = 'Archer'; + + constructor( + playerHealth: number, + playerStrength: number, + playerName: string, + playerWeapon: IWeapon, + playerSkills: ISkill[], + ) { + super(playerHealth, playerStrength, playerName, playerWeapon, playerSkills); + } +} diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts new file mode 100644 index 000000000..c3978dafd --- /dev/null +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -0,0 +1,29 @@ +import { Player } from '../abstract/Player'; +import { ISkill } from '../skills/ISkill'; +import { IWeapon } from '../weapon/IWeapon'; + +export class Knight extends Player { + protected _className: string = 'Knight'; + + constructor( + playerHealth: number, + playerStrength: number, + playerName: string, + playerWeapon: IWeapon, + playerSkills: ISkill[], + ) { + super(playerHealth, playerStrength, playerName, playerWeapon, playerSkills); + } + + public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { + let currentDamage: number = damage; + if (skill !== undefined && skill.name === 'ледяные стрелы') { + currentDamage -= skill.buff!.strength; + } + this._health -= currentDamage; + if (this._health <= 0) { + this._health = 0; + this._isAlive = false; + } + } +} diff --git a/rpgsaga/saga/src/game/classes/Wizard.ts b/rpgsaga/saga/src/game/classes/Wizard.ts new file mode 100644 index 000000000..56b6ea580 --- /dev/null +++ b/rpgsaga/saga/src/game/classes/Wizard.ts @@ -0,0 +1,17 @@ +import { Player } from '../abstract/Player'; +import { ISkill } from '../skills/ISkill'; +import { IWeapon } from '../weapon/IWeapon'; + +export class Wizard extends Player { + public _className: string = 'Wizard'; + + constructor( + playerHealth: number, + playerStrength: number, + playerName: string, + playerWeapon: IWeapon, + playerSkills: ISkill[], + ) { + super(playerHealth, playerStrength, playerName, playerWeapon, playerSkills); + } +} diff --git a/rpgsaga/saga/src/game/classes/index.ts b/rpgsaga/saga/src/game/classes/index.ts new file mode 100644 index 000000000..863f362b3 --- /dev/null +++ b/rpgsaga/saga/src/game/classes/index.ts @@ -0,0 +1,3 @@ +export * from './Archer'; +export * from './Knight'; +export * from './Wizard'; diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/ArcherFabric.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/ArcherFabric.ts new file mode 100644 index 000000000..4250382de --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/ArcherFabric.ts @@ -0,0 +1,33 @@ +import { Player } from '../../abstract/Player'; +import { Archer } from '../../classes'; +import { ISkill } from '../../skills/ISkill'; +import { getRandomArrayElement } from '../../utils/randomization'; +import { IWeapon } from '../../weapon/IWeapon'; +import { SkillFabric } from '../skillFabric/SkillFabric'; + +export class ArcherFabric { + private skillFabric = new SkillFabric(); + + public createArcher( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + const name: string = getRandomArrayElement(names)!; + const health: number = playerHealth; + const strength: number = playerStrength; + const weapon: IWeapon = playerWeapon; + + if (playerSkills !== null) { + return new Archer(health, strength, name, weapon, playerSkills); + } else { + const firstSkill = this.skillFabric.createSkillFromTemplate('ледяные стрелы')!; + firstSkill.usageCount = 2; + firstSkill.initialSkillUsage = 2; + const skills: ISkill[] = [firstSkill, this.skillFabric.createSkillFromTemplate('огненные стрелы')!]; + return new Archer(health, strength, name, weapon, skills); + } + } +} diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/KnightFabric.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/KnightFabric.ts new file mode 100644 index 000000000..850dc9c52 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/KnightFabric.ts @@ -0,0 +1,33 @@ +import { Player } from '../../abstract/Player'; +import { Knight } from '../../classes'; +import { ISkill } from '../../skills/ISkill'; +import { getRandomArrayElement } from '../../utils/randomization'; +import { IWeapon } from '../../weapon/IWeapon'; +import { SkillFabric } from '../skillFabric/SkillFabric'; + +export class KnightFabric { + private skillFabric = new SkillFabric(); + + public createKnight( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + const name: string = getRandomArrayElement(names)!; + const health: number = playerHealth; + const strength: number = playerStrength; + const weapon: IWeapon = playerWeapon; + + if (playerSkills !== null) { + return new Knight(health, strength, name, weapon, playerSkills); + } else { + const skills: ISkill[] = [ + this.skillFabric.createSkillFromTemplate('удар возмездия')!, + this.skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]; + return new Knight(health, strength, name, weapon, skills); + } + } +} diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts new file mode 100644 index 000000000..c57d1aa54 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts @@ -0,0 +1,74 @@ +import { Player } from '../../abstract/Player'; +import { ISkill } from '../../skills/ISkill'; +import { getRandomArrayElement, getRandomNumber } from '../../utils/randomization'; +import { IWeapon } from '../../weapon/IWeapon'; +import { WeaponFabric } from '../weaponsFabric/WeaponFabric'; +import { ArcherFabric } from './ArcherFabric'; +import { KnightFabric } from './KnightFabric'; +import { WizardFabric } from './WizardFabric'; + +export class PlayerFabric { + private weaponFabric = new WeaponFabric(); + private archerFabric = new ArcherFabric(); + private knightFabric = new KnightFabric(); + private wizardFabric = new WizardFabric(); + + public createPlayer( + playerClass: string, + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player | undefined { + const names: string[] = [ + 'Эльдар', + 'Артур', + 'Гэндальф', + 'Вильямс', + 'Агатон', + 'Аполлон', + 'Артемида', + 'Зевс', + 'Персей', + 'Феникс', + 'Элита', + 'Ирида', + 'Медея', + 'Орион', + 'Рафаэль', + 'Себастиан', + 'Эмиль', + 'Аврора', + 'Веста', + 'Лилия', + 'Мира', + ]; + switch (playerClass) { + case 'Knight': + return this.knightFabric.createKnight(names, playerHealth, playerStrength, playerWeapon, playerSkills); + case 'Archer': + return this.archerFabric.createArcher(names, playerHealth, playerStrength, playerWeapon, playerSkills); + case 'Wizard': + return this.wizardFabric.createWizard(names, playerHealth, playerStrength, playerWeapon, playerSkills); + } + } + + createRandomPlayer(): Player { + const playerFabric = new PlayerFabric(); + const classes: string[] = ['Knight', 'Archer', 'Wizard']; + const weapons: string[] = ['bow', 'sword', 'stick']; + const playerClass: string = getRandomArrayElement(classes)!; + const playerWeapon: IWeapon = this.weaponFabric.createRandomWeapon(getRandomArrayElement(weapons)!); + const health: number = getRandomNumber(125, 150); + const strength: number = getRandomNumber(10, 15); + return playerFabric.createPlayer(playerClass, health, strength, playerWeapon)!; + } + + createRandomPlayers(playersCount: number): Player[] { + const players: Player[] = []; + for (let i = 0; i < playersCount; i++) { + players.push(this.createRandomPlayer()); + } + return players; + } +} diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/WizardFabric.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/WizardFabric.ts new file mode 100644 index 000000000..77d6daa61 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/WizardFabric.ts @@ -0,0 +1,33 @@ +import { Player } from '../../abstract/Player'; +import { Wizard } from '../../classes'; +import { ISkill } from '../../skills/ISkill'; +import { getRandomArrayElement } from '../../utils/randomization'; +import { IWeapon } from '../../weapon/IWeapon'; +import { SkillFabric } from '../skillFabric/SkillFabric'; + +export class WizardFabric { + private skillFabric = new SkillFabric(); + + public createWizard( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + const name: string = getRandomArrayElement(names)!; + const health: number = playerHealth; + const strength: number = playerStrength; + const weapon: IWeapon = playerWeapon; + + if (playerSkills !== null) { + return new Wizard(health, strength, name, weapon, playerSkills); + } else { + const skills: ISkill[] = [ + this.skillFabric.createSkillFromTemplate('заворожение')!, + this.skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]; + return new Wizard(health, strength, name, weapon, skills); + } + } +} diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/index.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/index.ts new file mode 100644 index 000000000..a3aa5c7a5 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/index.ts @@ -0,0 +1,4 @@ +export * from './ArcherFabric'; +export * from './KnightFabric'; +export * from './PlayerFabric'; +export * from './WizardFabric'; diff --git a/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts new file mode 100644 index 000000000..72781e238 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts @@ -0,0 +1,97 @@ +import { Player } from '../../abstract/Player'; +import { ISkill } from '../../skills/ISkill'; + +export class SkillFabric { + private skillsTemplate: ISkill[] = [ + { + name: 'огненные стрелы', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + effect: (caster: Player, opponent: Player) => { + caster.damageUp(2); + }, + buff: { + strength: 2, + }, + }, + { + name: 'ледяные стрелы', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + turns: 3, + initialTurns: 3, + effect: (caster: Player, opponent: Player) => { + caster.damageUp(3); + }, + buff: { + strength: 3, + }, + }, + { + name: 'удар возмездия', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + damage: (caster: Player) => caster.strength * 1.3 + caster.weapon.damage, + effect: (caster: Player, opponent: Player) => { + const weaponDamage = caster.weapon ? caster.weapon.damage : 0; + opponent.takeDamage(caster.strength * 1.3 + weaponDamage, caster); + }, + }, + { + name: 'заворожение', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + effect: (caster: Player, opponent: Player) => { + opponent.skipTurns(1); + }, + }, + ]; + + public createSkill( + skillName: string, + skillDamage: (caster: Player) => number | undefined, + isUsedSKill: boolean, + skillUsageCount: number, + skillInitialUsage: number, + skillTurns: number | undefined = undefined, + skillInitialTurns: number | undefined = undefined, + skillEffect: (caster: Player, opponent: Player) => void, + skillBuff: { strength: number } | undefined, + ) { + const skill: ISkill = { + name: skillName, + damage: skillDamage, + isUsed: isUsedSKill, + usageCount: skillUsageCount, + initialSkillUsage: skillInitialUsage, + turns: skillTurns, + initialTurns: skillInitialTurns, + effect: skillEffect, + buff: skillBuff, + }; + return skill; + } + + public createSkillFromTemplate(templateName: string): ISkill | null { + const skillTemplate = this.skillsTemplate.find(skill => skill.name === templateName); + if (!skillTemplate) { + return null; + } + + return this.createSkill( + skillTemplate.name, + skillTemplate.damage!, + skillTemplate.isUsed, + skillTemplate.usageCount, + skillTemplate.initialSkillUsage, + skillTemplate.turns, + skillTemplate.initialTurns, + skillTemplate.effect!, + skillTemplate.buff, + ); + } +} diff --git a/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts new file mode 100644 index 000000000..1f0cb5544 --- /dev/null +++ b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts @@ -0,0 +1,78 @@ +import { getRandomArrayElement, getRandomNumber } from '../../utils/randomization'; +import { IWeapon } from '../../weapon/IWeapon'; + +export class WeaponFabric implements IWeapon { + private names: object = { + sword: ['Dragonsbane', 'Stormbringer', 'Aethelred'], + stick: ['Oak Staff', 'Elderwood Branch', "Shepherd's Crook"], + bow: ["Hunter's Bow", 'Longbow', 'Shortbow'], + }; + private types: string[] = ['огонь', 'яд', 'лёд']; + + private _name: string = ''; + private _typeOfDamage: string = ''; + private _damage: number = 0; + + public get name(): string { + return this._name; + } + + public get typeOfDamage(): string { + return this._typeOfDamage; + } + + public get damage(): number { + return this._damage; + } + + public createWeapon(weaponType: string, weaponDamageType: string, weaponName: string, weaponDamage: number) { + let weapon: IWeapon; + this._name = weaponName; + this._damage = weaponDamage; + this._typeOfDamage = weaponDamageType; + switch (weaponType.toLowerCase()) { + case 'sword': + weapon = { + name: this.name, + damage: this.damage, + typeOfDamage: this.typeOfDamage, + }; + break; + case 'stick': + weapon = { + name: this.name, + damage: this.damage, + typeOfDamage: this.typeOfDamage, + }; + break; + case 'bow': + weapon = { + name: this.name, + damage: this.damage, + typeOfDamage: this.typeOfDamage, + }; + break; + default: + weapon = { + name: 'fists', + damage: 3, + typeOfDamage: '-', + }; + } + return weapon; + } + + public createRandomWeapon(weaponType: string): IWeapon { + const damageType = getRandomArrayElement(this.types)!; + const namesArr: string[] = this.names[weaponType.toLowerCase() as keyof typeof this.names]; + + if (!namesArr) { + return this.createWeapon('fists', '-', 'fists', 3); + } + + const name = + this.names[weaponType.toLowerCase() as keyof typeof this.names][Math.floor(Math.random() * namesArr.length)]; + const damage = getRandomNumber(5, 10); + return this.createWeapon(weaponType, damageType, name, damage); + } +} diff --git a/rpgsaga/saga/src/game/gameplay/Game.ts b/rpgsaga/saga/src/game/gameplay/Game.ts new file mode 100644 index 000000000..448ae46cf --- /dev/null +++ b/rpgsaga/saga/src/game/gameplay/Game.ts @@ -0,0 +1,97 @@ +import { Player } from '../abstract/Player'; +import { PlayerFabric } from '../fabrics/playersFabrics'; +import { Logger } from '../utils/output/Logger'; + +export class Game { + private playerFabric = new PlayerFabric(); + private _players: Player[] = []; + private logger: Logger; + + constructor(playerCount: number, player: Player | undefined = undefined, logger: Logger) { + this._players = this.playerFabric.createRandomPlayers(playerCount); + this.logger = logger; + if (player !== undefined) { + this._players.push(player); + } + } + + public get players(): Player[] { + return this._players; + } + + public async start() { + this.logger.messageLog('Игра началась!'); + let listOfPlayers = 'Список участников: \n\n'; + listOfPlayers += this._players.map(player => `(${player.className}) ${player.name}`).join('\n\n'); + this.logger.messageLog(listOfPlayers); + await this.tournament(this._players); + this.logger.messageLog(`Победитель: (${this._players[0].className}) ${this._players[0].name}`); + } + + public async tournament(players: Player[]): Promise { + if (players.length === 1) { + return players[0]; + } + + const nextRoundPlayers: Player[] = []; + for (let i = 0; i < players.length; i += 2) { + const player1 = players[i]; + const player2 = players[i + 1]; + const winner = await this.battle([player1, player2]); + nextRoundPlayers.push(winner); + player1.reset(); + player2.reset(); + } + + return this.tournament(nextRoundPlayers); + } + + public async battle(fighters: Player[]): Promise { + this.logger.messageLog(`(${fighters[0].name}) vs (${fighters[1].name})`); + + let turn = 0; + + while (fighters[0].health > 0 && fighters[1].health > 0) { + const attackerIndex = turn % 2; + const defenderIndex = (turn + 1) % 2; + const attacker = fighters[attackerIndex]; + const defender = fighters[defenderIndex]; + + if (defender.isAlive) { + if (attacker.countOfSkipingTurns === 0) { + attacker.attack(defender); + this.logger.attackLog(attacker, defender); + } else { + attacker.attack(defender); + this.logger.skipTurnLog(attacker, defender); + } + + if (!defender.isAlive) { + this.logger.deathLog(defender); + break; + } + } + + if (Math.random() < 0.4 && attacker.isAlive && defender.isAlive) { + attacker.choseSkill(); + if (attacker.currentSkill!.usageCount! > 0) { + attacker.useSkill(defender); + this.logger.skillLog(attacker, defender); + } + } + + await this.delay(200); + turn++; + } + + this.updatePlayersArray(); + return fighters.find(player => player.health > 0)!; + } + + private delay(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); + } + private updatePlayersArray() { + this._players = this._players.filter(player => player.isAlive); + } +} diff --git a/rpgsaga/saga/src/game/skills/ISkill.ts b/rpgsaga/saga/src/game/skills/ISkill.ts new file mode 100644 index 000000000..04ead1776 --- /dev/null +++ b/rpgsaga/saga/src/game/skills/ISkill.ts @@ -0,0 +1,15 @@ +import { Player } from '../abstract/Player'; + +export interface ISkill { + name: string; + damage?: (caster: Player) => number | undefined; + isUsed: boolean; + usageCount: number; + initialSkillUsage: number; + turns?: number; + initialTurns?: number; + effect?: (caster: Player, opponent: Player) => void; + buff?: { + strength: number; + }; +} diff --git a/rpgsaga/saga/src/game/utils/input/createCharacter.ts b/rpgsaga/saga/src/game/utils/input/createCharacter.ts new file mode 100644 index 000000000..5965745e7 --- /dev/null +++ b/rpgsaga/saga/src/game/utils/input/createCharacter.ts @@ -0,0 +1,118 @@ +import { PlayerFabric } from '../../fabrics/playersFabrics/index'; +import { SkillFabric } from '../../fabrics/skillFabric/SkillFabric'; +import { WeaponFabric } from '../../fabrics/weaponsFabric/WeaponFabric'; +import { Game } from '../../gameplay/Game'; +import { ISkill } from '../../skills/ISkill'; +import { IWeapon } from '../../weapon/IWeapon'; +import { Logger } from '../output/Logger'; +import { readAnswer } from '../question/readAnswer'; + +export async function createCharacter(numberOfPlayers: number): Promise { + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const logger = new Logger(); + + let playerType: string; + let playerHealth: number = 0; + let playerStrength: number = 0; + let playerWeapon: IWeapon; + let playerSkills: ISkill[] = []; + + const playerFabric = new PlayerFabric(); + const types: string[] = ['Knight', 'Archer', 'Wizard']; + const weapons: string[] = ['bow', 'sword', 'stick']; + const skillNames: string[] = ['огненные стрелы', 'ледяные стрелы', 'удар возмездия', 'заворожение']; + + async function askForClass(): Promise { + const playerClass: string = await readAnswer('Выберите класс своего героя: 1. Knight, 2. Archer, 3. Wizard: '); + const number: number = parseInt(playerClass); + if (isNaN(number) || number < 1 || number > 3) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForClass(); + } else { + playerType = types[number - 1]; + await askForHealth(); + } + } + + async function askForHealth(): Promise { + const healthInput: string = await readAnswer('Напишите количество здоровья для своего героя (от 125 до 150): '); + const number: number = parseInt(healthInput); + if (isNaN(number) || number < 125 || number > 150) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForHealth(); + } else { + playerHealth = number; + await askForStrength(); + } + } + + async function askForStrength(): Promise { + const strengthInput: string = await readAnswer('Напишите количество силы для своего героя (от 10 до 15): '); + const number: number = parseInt(strengthInput); + if (isNaN(number) || number < 10 || number > 15) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForStrength(); + } else { + playerStrength = number; + await askForWeapon(); + } + } + + async function askForWeapon(): Promise { + const playerClass: string = await readAnswer('Выберите оружие своего героя: 1. меч, 2. лук, 3. посох: '); + const number: number = parseInt(playerClass); + if (isNaN(number) || number < 1 || number > 3) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForWeapon(); + } else { + playerWeapon = weaponFabric.createRandomWeapon(weapons[number - 1]); + await askForSkills(); + } + } + + async function askForSkills(): Promise { + const playerClass: string = await readAnswer( + 'Выберите скиллы своего героя: 1. огненные стрелы, 2. ледяные стрелы, 3. удар возмездия, 4. заворожение. \nДля старта со стандартными навыками класса, напишите 5. Для выхода напишите 6 : ', + ); + const number: number = parseInt(playerClass); + if (isNaN(number) || number < 1 || number > 6) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForSkills(); + } else if (number < 5 && number > 0) { + if (playerSkills.length > 2) { + console.log('У вас уже максимальное количество скиллов'); + } else { + playerSkills.push(skillFabric.createSkillFromTemplate(skillNames[number - 1])!); + } + await askForSkills(); + } else if (number === 6) { + if (playerSkills.length > 0) { + return; + } else { + console.log('Выберите хотя бы один скилл'); + await askForSkills(); + } + } else { + return; + } + } + + await askForClass(); + + if (playerSkills.length !== 0) { + const game = new Game( + numberOfPlayers - 1, + playerFabric.createPlayer(playerType!, playerHealth, playerStrength, playerWeapon!, playerSkills), + logger, + ); + await game.start(); + } else { + const game = new Game( + numberOfPlayers - 1, + playerFabric.createPlayer(playerType!, playerHealth, playerStrength, playerWeapon!), + logger, + ); + await game.start(); + } +} diff --git a/rpgsaga/saga/src/game/utils/input/createGame.ts b/rpgsaga/saga/src/game/utils/input/createGame.ts new file mode 100644 index 000000000..d13fb00b6 --- /dev/null +++ b/rpgsaga/saga/src/game/utils/input/createGame.ts @@ -0,0 +1,39 @@ +import { Game } from '../../gameplay/Game'; +import { Logger } from '../output/Logger'; +import { readAnswer } from '../question/readAnswer'; +import { createCharacter } from './createCharacter'; + +export function createGame(): void { + const logger = new Logger(); + + let number: number; + async function askForPlayers() { + const inputNumber: string = await readAnswer('Введите число игроков (должно делиться на 4): '); + number = parseInt(inputNumber); + if (isNaN(number) || number < 1 || number % 4 !== 0) { + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForPlayers(); + } else { + await askForCreating(); + } + } + + async function askForCreating() { + const inputString: string = await readAnswer('Хотите ли вы создать своего персонажа? (да/нет) '); + switch (inputString.toLowerCase()) { + case 'да': + createCharacter(number); + break; + case 'нет': + const game = new Game(number, undefined, logger); + await game.start(); + break; + default: + console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); + await askForCreating(); + break; + } + } + + askForPlayers(); +} diff --git a/rpgsaga/saga/src/game/utils/output/Logger.ts b/rpgsaga/saga/src/game/utils/output/Logger.ts new file mode 100644 index 000000000..e61523297 --- /dev/null +++ b/rpgsaga/saga/src/game/utils/output/Logger.ts @@ -0,0 +1,56 @@ +import { Player } from '../../abstract/Player'; + +export class Logger { + //static _instance: Logger; + + constructor() {} + + //public get instance(): Logger { + // if (!Logger._instance) { + // Logger._instance = new Logger(); + // } + // return Logger._instance; + //} + + public messageLog(message: string): void { + const timestamp: string = new Date().toISOString(); + const logEntry: string = `${timestamp}: ${message}\n`; + console.log(logEntry); + } + + public attackLog(attacker: Player, defender: Player): void { + const timestamp: string = new Date().toISOString(); + const message: string = `(${attacker.className}) ${attacker.name} наносит урон ${ + attacker.strength + attacker.weapon!.damage! + } на ${defender.name} (${defender.className})`; + const logEntry: string = `${timestamp}: ${message}\n`; + console.log(logEntry); + } + + public skillLog(attacker: Player, defender: Player): void { + const timestamp: string = new Date().toISOString(); + let message: string = ''; + message += `(${attacker.className}) ${attacker.name} использует ${attacker.currentSkill?.name} на ${defender.name} (${defender.className}) `; + if (attacker.currentSkill?.damage) { + message += `и наносит урон ${attacker.currentSkill.damage(attacker)}`; + } + const logEntry: string = `${timestamp}: ${message}\n`; + console.log(logEntry); + } + + public skipTurnLog(attacker: Player, defender: Player): void { + const timestamp: string = new Date().toISOString(); + const message: string = `(${attacker.className}) ${attacker.name} пропускает ход из-за ${ + defender.currentSkill!.name + }`; + const logEntry: string = `${timestamp}: ${message}\n`; + console.log(logEntry); + } + + public deathLog(warrior: Player): void { + const timestamp: string = new Date().toISOString(); + const message: string = `(${warrior.className}) ${warrior.name} умирает`; + const logEntry: string = `${timestamp}: ${message}\n`; + console.log(logEntry); + } +} diff --git a/rpgsaga/saga/src/game/utils/question/readAnswer.ts b/rpgsaga/saga/src/game/utils/question/readAnswer.ts new file mode 100644 index 000000000..f311310da --- /dev/null +++ b/rpgsaga/saga/src/game/utils/question/readAnswer.ts @@ -0,0 +1,15 @@ +import * as readline from 'readline'; + +export function readAnswer(query: string): Promise { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + return new Promise(resolve => { + rl.question(query, answer => { + resolve(answer); + rl.close(); + }); + }); +} diff --git a/rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts b/rpgsaga/saga/src/game/utils/randomization/getRandomArrayElement.ts similarity index 68% rename from rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts rename to rpgsaga/saga/src/game/utils/randomization/getRandomArrayElement.ts index ed9fcd7e1..c237f711d 100644 --- a/rpgsaga/saga/src/utils/randomization/getRandomArrayElement.ts +++ b/rpgsaga/saga/src/game/utils/randomization/getRandomArrayElement.ts @@ -2,6 +2,6 @@ export function getRandomArrayElement(arr: T[]): T | undefined { if (arr.length === 0) { return undefined; } - const randomIndex = Math.floor(Math.random() * arr.length); + const randomIndex: number = Math.floor(Math.random() * arr.length); return arr[randomIndex]; } diff --git a/rpgsaga/saga/src/game/utils/randomization/getRandomNumber.ts b/rpgsaga/saga/src/game/utils/randomization/getRandomNumber.ts new file mode 100644 index 000000000..83286e56e --- /dev/null +++ b/rpgsaga/saga/src/game/utils/randomization/getRandomNumber.ts @@ -0,0 +1,13 @@ +export function getRandomNumber(min: number, max: number): number { + if (min > max) { + return -1; + } + if (min === max) { + return min; + } + + const range: number = max - min + 1; + const randomNumber: number = Math.floor(Math.random() * range) + min; + + return randomNumber; +} diff --git a/rpgsaga/saga/src/game/utils/randomization/index.ts b/rpgsaga/saga/src/game/utils/randomization/index.ts new file mode 100644 index 000000000..6c9e0a1c1 --- /dev/null +++ b/rpgsaga/saga/src/game/utils/randomization/index.ts @@ -0,0 +1,2 @@ +export * from './getRandomArrayElement'; +export * from './getRandomNumber'; diff --git a/rpgsaga/saga/src/game/weapon/IWeapon.ts b/rpgsaga/saga/src/game/weapon/IWeapon.ts new file mode 100644 index 000000000..22e513405 --- /dev/null +++ b/rpgsaga/saga/src/game/weapon/IWeapon.ts @@ -0,0 +1,5 @@ +export interface IWeapon { + get name(): string; + get damage(): number; + get typeOfDamage(): string; +} diff --git a/rpgsaga/saga/src/gameplay/Game.ts b/rpgsaga/saga/src/gameplay/Game.ts deleted file mode 100644 index e1f18968e..000000000 --- a/rpgsaga/saga/src/gameplay/Game.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Player } from '../abstract/Player'; -import { Knight } from '../classes/Knight'; -import { Archer } from '../classes/Archer'; -import { Vizard } from '../classes/Vizard'; -import { Logger } from '../utils/output/Logger'; -import { getRandomNumber } from '../utils/randomization/getRandomNumber'; -import { getRandomArrayElement } from '../utils/randomization/getRandomArrayElement'; - -export class Game { - private players: Player[] = []; - private initialHealth: number[] = []; - private initialStrength: number[] = []; - - constructor(playerCount: number) { - this.players = []; - const names = [ - 'Эльдар', - 'Артур', - 'Гэндальф', - 'Вильямс', - 'Агатон', - 'Аполлон', - 'Артемида', - 'Зевс', - 'Персей', - 'Феникс', - 'Элита', - 'Ирида', - 'Медея', - 'Орион', - 'Рафаэль', - 'Себастиан', - 'Эмиль', - 'Аврора', - 'Веста', - 'Лилия', - 'Мира', - ]; - - for (let i = 0; i < playerCount; i++) { - const name = getRandomArrayElement(names); - const health = getRandomNumber(75, 100); - const strength = getRandomNumber(15, 20); - const player = this.createPlayer(name, health, strength); - this.players.push(player); - this.initialHealth.push(player.healthPoints); - this.initialStrength.push(player.strengthPoints); - } - } - - public get playersCount() { - return this.players; - } - - private createPlayer(name: string, health: number, strength: number): Player { - const types = [Knight, Archer, Vizard]; - const PlayerClass = getRandomArrayElement(types); - return new PlayerClass(health, strength, name); - } - - public async start() { - Logger.log('Игра началась!'); - await this.tournament(this.players); - Logger.log(`Победитель: ${this.players[0].playerName}`); - } - - private async tournament(players: Player[]): Promise { - if (players.length === 1) { - return players[0]; - } - - const nextRoundPlayers: Player[] = []; - for (let i = 0; i < players.length; i += 2) { - const player1 = players[i]; - const player2 = players[i + 1]; - const winner = await this.battle([player1, player2]); - nextRoundPlayers.push(winner); - player1.healthPoints = this.initialHealth[this.players.indexOf(player1)]; - player2.healthPoints = this.initialHealth[this.players.indexOf(player2)]; - player1.strengthPoints = this.initialStrength[this.players.indexOf(player1)]; - player2.strengthPoints = this.initialStrength[this.players.indexOf(player2)]; - player1.playerSkillUsed = false; - player2.playerSkillUsed = false; - if (player1 instanceof Vizard && player1.countOfVizardSkills >= 1) { - player1.countOfVizardSkills = 0; - } else if (player2 instanceof Vizard && player2.countOfVizardSkills >= 1) { - player2.countOfVizardSkills = 0; - } - } - - return this.tournament(nextRoundPlayers); - } - - private async battle(fighters: Player[]): Promise { - Logger.log(`(${fighters[0].playerName}) vs (${fighters[1].playerName})`); - - let turn = 0; - - while (fighters[0].healthPoints > 0 && fighters[1].healthPoints > 0) { - const attackerIndex = turn % 2; - const defenderIndex = (turn + 1) % 2; - const attacker = fighters[attackerIndex]; - const defender = fighters[defenderIndex]; - - if (defender.isAlivePlayer) { - Logger.log(attacker.attack(defender)); - - if (!defender.isAlivePlayer) { - Logger.log(defender.takeDamage(1)); - break; - } - } - - if (Math.random() < 0.5 && attacker.isAlivePlayer && defender.isAlivePlayer) { - if (attacker instanceof Knight && !attacker.playerSkillUsed) { - Logger.log(attacker.useSkill(defender)); - } else if (attacker instanceof Archer && !attacker.playerSkillUsed) { - Logger.log(attacker.useSkill(defender)); - } else if (attacker instanceof Vizard && !attacker.playerSkillUsed && attacker.countOfVizardSkills < 1) { - Logger.log(attacker.useSkill(defender)); - } - } - - await this.delay(200); - turn++; - } - - return fighters.find(player => player.healthPoints > 0)!; - } - - private delay(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); - } -} diff --git a/rpgsaga/saga/src/index.ts b/rpgsaga/saga/src/index.ts index aea9960f8..860f16e62 100644 --- a/rpgsaga/saga/src/index.ts +++ b/rpgsaga/saga/src/index.ts @@ -1,4 +1,4 @@ -import { input } from './utils/input/input'; +import { createGame } from './game/utils/input/createGame'; import { taskA, taskB, output } from './funcAndClasses/function/function'; import { Person } from './funcAndClasses/classes/abstract/Person'; import { Accountant } from './funcAndClasses/classes/workers/Accountant'; @@ -16,4 +16,4 @@ for (const pers of arrOfPersons) { console.log(pers.workRespons()); } -input(); +createGame(); diff --git a/rpgsaga/saga/src/skills/ISkills.ts b/rpgsaga/saga/src/skills/ISkills.ts deleted file mode 100644 index e4cf26d5f..000000000 --- a/rpgsaga/saga/src/skills/ISkills.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Player } from '../abstract/Player'; - -export interface ISkills { - name: string; - damage?: number; - turns?: number; - isAvailable: boolean; - effect: (opponent: Player) => number; -} diff --git a/rpgsaga/saga/src/utils/input/input.ts b/rpgsaga/saga/src/utils/input/input.ts deleted file mode 100644 index 5f54f6da8..000000000 --- a/rpgsaga/saga/src/utils/input/input.ts +++ /dev/null @@ -1,26 +0,0 @@ -import * as readline from 'readline'; - -import { Game } from '../../gameplay/Game'; - -export function input(): void { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - }); - - function askForPlayers() { - rl.question('Введите число игроков (должно делиться на 4): ', inputNumber => { - const number = parseInt(inputNumber); - if (isNaN(number) || number < 1 || number % 4 !== 0) { - console.log('Некорректный ввод. Пожалуйста, попробуйте снова.'); - askForPlayers(); - } else { - const game = new Game(number); - game.start(); - rl.close(); - } - }); - } - - askForPlayers(); -} diff --git a/rpgsaga/saga/src/utils/output/Logger.ts b/rpgsaga/saga/src/utils/output/Logger.ts deleted file mode 100644 index 2cf2173e5..000000000 --- a/rpgsaga/saga/src/utils/output/Logger.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class Logger { - static log(message: string): void { - const timestamp = new Date().toISOString(); - const logEntry = `${timestamp}: ${message}\n`; - console.log(logEntry); - } -} diff --git a/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts b/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts deleted file mode 100644 index 66da01837..000000000 --- a/rpgsaga/saga/src/utils/randomization/getRandomNumber.ts +++ /dev/null @@ -1,13 +0,0 @@ -export function getRandomNumber(min: number, max: number): number { - if (min > max) { - throw new Error('Minimum value cannot be greater than maximum value.'); - } - if (min === max) { - return min; - } - - const range = max - min + 1; - const randomNumber = Math.floor(Math.random() * range) + min; - - return randomNumber; -} diff --git a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts index 3a6c4f112..bdaa39e6e 100644 --- a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -1,46 +1,144 @@ -import { Archer } from '../../src/classes/Archer'; +import { Archer, Wizard } from '../../src/game/classes'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; +import { WeaponFabric } from '../../src/game/fabrics/weaponsFabric/WeaponFabric'; describe('Archer class methods tests', () => { it('Constructor test', () => { - let newArcher = new Archer(75, 25, 'Ibragim'); - expect(newArcher.healthPoints).toEqual(75); - expect(newArcher.strengthPoints).toBe(25); - expect(newArcher.playerName).toBe('Ibragim'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newArcher = new Archer(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('bow'), [ + skillFabric.createSkillFromTemplate('огненные стрелы')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + expect(newArcher).toBeInstanceOf(Archer); + expect(newArcher.health).toBe(75); + expect(newArcher.strength).toBe(25); + expect(newArcher.name).toBe('Ibragim'); }); + describe('Get methods tests', () => { - let newArcher = new Archer(75, 25, 'Ibragim'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newArcher = new Archer(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('bow'), [ + skillFabric.createSkillFromTemplate('огненные стрелы')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + it('Health get test', () => { - expect(newArcher.healthPoints).toEqual(75); + expect(newArcher.health).toBe(75); }); it('Strength get test', () => { - expect(newArcher.strengthPoints).toBe(25); + expect(newArcher.strength).toBe(25); }); it('Name get test', () => { - expect(newArcher.playerName).toBe('Ibragim'); + expect(newArcher.name).toBe('Ibragim'); }); - }); - describe('Archer methods tests', () => { - let newArcher = new Archer(75, 25, 'Ibragim'); - let opponent = new Archer(86, 26, 'Mustafa'); - it('Should change the propertie "skillUsed" to true', () => { - newArcher.useSkill(opponent); - expect(newArcher.playerSkillUsed).toEqual(true); + it('ClassName get test', () => { + expect(newArcher.className).toBe('Archer'); + }); + it('IsAlive get test', () => { + expect(newArcher.isAlive).toBe(true); + }); + it('IsSkillUsed get test', () => { + expect(newArcher.isSkillUsed).toBe(false); + }); + it('IsSkillUsed get test after using skill', () => { + newArcher.choseSkill(); + newArcher.useSkill(newArcher); + expect(newArcher.isSkillUsed).toBe(true); + }); + it('InitialHealth get test', () => { + expect(newArcher.initialHealth).toBe(75); + }); + it('InitialStrength get test', () => { + expect(newArcher.initialStrength).toBe(25); + }); + it('CountOfSkippingTurns get test', () => { + expect(newArcher.countOfSkipingTurns).toBe(0); + }); + it('CountOfSkippingTurns get test after using skipping spell', () => { + const opponent = new Wizard(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + opponent.useSkill(newArcher, 'заворожение'); + expect(newArcher.countOfSkipingTurns).toBe(1); }); }); + describe('Archer methods tests', () => { - let newArcher = new Archer(75, 25, 'Ibragim'); - let opponent = new Archer(86, 26, 'Mustafa'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newArcher = new Archer(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('bow'), [ + skillFabric.createSkillFromTemplate('огненные стрелы')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + const opponent = new Archer(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('bow'), [ + skillFabric.createSkillFromTemplate('огненные стрелы')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + it('Should return health after an attack whithout using a skill', () => { newArcher.attack(opponent); - expect(opponent.healthPoints).toEqual(86 - newArcher.strengthPoints); + expect(opponent.health).toBe(86 - (newArcher.strength + newArcher.weapon.damage)); }); + it('Health should decrease by the number of damage units', () => { - newArcher.takeDamage(45); - expect(newArcher.healthPoints).toEqual(75 - 45); + newArcher.takeDamage(45, opponent, opponent.currentSkill); + expect(newArcher.health).toBe(75 - 45); + }); + + it('Strength should icnrease', () => { + newArcher.damageUp(2); + expect(newArcher.strength).toBe(27); }); + + it('Should change the propertie "skillUsed" to true', () => { + newArcher.choseSkill(); + newArcher.useSkill(opponent); + expect(newArcher.isSkillUsed).toBe(true); + }); + + it('Health should icnrease', () => { + newArcher.heal(10); + expect(newArcher.health).toBe(40); + }); + + it('Health should be equal initialHealth', () => { + newArcher.heal(100); + expect(newArcher.health).toBe(newArcher.initialHealth); + }); + it('Ibragim should DIE.', () => { - newArcher.takeDamage(45); - expect(newArcher.isAlivePlayer).toEqual(false); + newArcher.takeDamage(newArcher.initialHealth, opponent, opponent.currentSkill); + expect(newArcher.isAlive).toBe(false); + expect(newArcher.health).toBe(0); + }); + + it('Ibragim health should be equal 0.', () => { + newArcher.takeDamage(1000, opponent, opponent.currentSkill); + expect(newArcher.health).toBe(0); + }); + + it('Ibragim should reset.', () => { + newArcher.reset(); + expect(newArcher.health).toBe(newArcher.initialHealth); + expect(newArcher.strength).toBe(newArcher.initialStrength); + expect(newArcher.isSkillUsed).toBe(false); + newArcher.skills!.forEach(skill => { + expect(skill.usageCount).toBe(skill.initialSkillUsage); + expect(skill.isUsed).toBe(false); + expect(skill.turns).toBe(skill.initialTurns); + }); + }); + + it('Ibragim strength should be equal initialStrength.', () => { + newArcher.useSkill(opponent, 'ледяные стрелы'); + newArcher.attack(opponent); + newArcher.useSkill(opponent, 'огненные стрелы'); + newArcher.attack(opponent); + newArcher.attack(opponent); + expect(newArcher.strength).toBe(27); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/ArcherFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/ArcherFabric.spec.ts new file mode 100644 index 000000000..dd7be64f4 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/ArcherFabric.spec.ts @@ -0,0 +1,73 @@ +import { ArcherFabric } from '../../src/game/fabrics/playersFabrics'; +import { Archer } from '../../src/game/classes'; +import { IWeapon } from '../../src/game/weapon/IWeapon'; +import { ISkill } from '../../src/game/skills/ISkill'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; + +class MockWeapon implements IWeapon { + name: string = 'Mock Weapon'; + typeOfDamage: string = 'Mock type'; + damage: number = 10; +} + +class MockSkill implements ISkill { + name: string = 'Mock Skill'; + description: string = 'Mock description'; + isUsed: boolean = false; + usageCount: number = 0; + initialSkillUsage: number = 0; + effect(): void {} +} + +class MockSkillFabric extends SkillFabric { + createSkillFromTemplate(templateName: string): ISkill | null { + if (templateName === 'ледяные стрелы') { + return new MockSkill(); + } else if (templateName === 'огненные стрелы') { + return new MockSkill(); + } + return null; + } +} + +describe('ArcherFabric tests', () => { + let archerFabric: ArcherFabric; + let mockSkillFabric: MockSkillFabric; + let mockWeapon: MockWeapon; + + beforeEach(() => { + mockSkillFabric = new MockSkillFabric(); + archerFabric = new ArcherFabric(); + mockWeapon = new MockWeapon(); + (archerFabric as any).skillFabric = mockSkillFabric; + }); + + it('Should create an archer with provided skills', () => { + const mockSkills: ISkill[] = [new MockSkill(), new MockSkill()]; + const newArcher = archerFabric.createArcher(['Alice', 'Bob'], 100, 20, mockWeapon, mockSkills); + expect(newArcher).toBeInstanceOf(Archer); + expect(newArcher.health).toBe(100); + expect(newArcher.strength).toBe(20); + expect(newArcher.weapon).toBe(mockWeapon); + expect(newArcher.skills).toBe(mockSkills); + }); + + it('Should create an archer with default skills if no skills are provided', () => { + const newArcher = archerFabric.createArcher(['Alice', 'Bob'], 100, 20, mockWeapon); + expect(newArcher).toBeInstanceOf(Archer); + expect(newArcher.health).toBe(100); + expect(newArcher.strength).toBe(20); + expect(newArcher.weapon).toBe(mockWeapon); + expect(newArcher.skills.length).toBe(2); + expect(newArcher.skills[0].usageCount).toBe(2); + expect(newArcher.skills[0].initialSkillUsage).toBe(2); + }); + + it('Should select a random name from the provided names array', () => { + const names = ['Alice', 'Bob', 'Charlie']; + const newArcher1 = archerFabric.createArcher(names, 100, 20, mockWeapon); + const newArcher2 = archerFabric.createArcher(names, 100, 20, mockWeapon); + expect(names.includes(newArcher1.name)).toBe(true); + expect(names.includes(newArcher2.name)).toBe(true); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/Game.spec.ts b/rpgsaga/saga/tests/sagaTests/Game.spec.ts index 467972506..90da669c1 100644 --- a/rpgsaga/saga/tests/sagaTests/Game.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Game.spec.ts @@ -1,27 +1,95 @@ -import { Game } from '../../src/gameplay/Game'; - -describe('Game class methods tests', () => { - it('Constructor test', () => { - let newGame = new Game(4); - expect(newGame.playersCount.length).toEqual(4); - }); - it('should create players with random names, health, and strength', () => { - let newGame = new Game(2); - expect( - newGame.playersCount.every(p => p.playerName !== undefined && p.healthPoints > 0 && p.strengthPoints > 0), - ).toBe(true); - }); - it('should create players of different types (Knight, Archer, Vizard)', () => { - let newGame = new Game(10); - const playerTypes = newGame.playersCount.map(p => p.constructor.name); - expect(playerTypes.includes('Knight')).toBe(true); - expect(playerTypes.includes('Archer')).toBe(true); - expect(playerTypes.includes('Vizard')).toBe(true); - }); - describe('Get methods tests', () => { - let newGame = new Game(20); - it('Players get test', () => { - expect(newGame.playersCount.length).toEqual(20); - }); +import { Game } from '../../src/game/gameplay/Game'; +import { PlayerFabric } from '../../src/game/fabrics/playersFabrics'; +import { Player } from '../../src/game/abstract/Player'; +import { Logger } from '../../src/game/utils/output/Logger'; + +class MockLogger extends Logger { + messages: string[] = []; + attackLogs: string[] = []; + skillLogs: string[] = []; + deathLogs: string[] = []; + skipTurnLogs: string[] = []; + + messageLog(message: string): void { + this.messages.push(message); + } + attackLog(attacker: Player, defender: Player): void { + this.attackLogs.push(`${attacker.name} атакует ${defender.name}`); + } + skillLog(attacker: Player, defender: Player): void { + this.skillLogs.push(`${attacker.name} использует скилл на ${defender.name}`); + } + deathLog(player: Player): void { + this.deathLogs.push(`${player.name} умер`); + } + skipTurnLog(attacker: Player, defender: Player): void { + this.skipTurnLogs.push(`${attacker.name} пропускает ходы`); + } +} + +describe('Game tests', () => { + let game: Game; + let logger: MockLogger; + const playerFabric = new PlayerFabric(); + + beforeEach(() => { + logger = new MockLogger(); + game = new Game(2, undefined, logger); + }); + + it('Should start a game with two players', async () => { + await game.start(); + expect(logger.messages.length).toBeGreaterThan(0); + expect(logger.messages[0]).toBe('Игра началась!'); + expect(logger.messages[1]).toContain('Список участников'); + expect(logger.messages).toContain(`Победитель: (${game.players[0].className}) ${game.players[0].name}`); + }); + + it('Should handle a tournament with multiple players', async () => { + const players = playerFabric.createRandomPlayers(4); + const result = await game.tournament(players); + expect(result).toBeDefined(); + expect(result.health).toBeGreaterThan(0); + }); + + it('Should simulate a battle between two players', async () => { + const player1 = playerFabric.createRandomPlayer(); + const player2 = playerFabric.createRandomPlayer(); + const winner = await game.battle([player1, player2]); + expect(winner).toBeDefined(); + expect(winner.health).toBeGreaterThan(0); + expect(logger.attackLogs.length).toBeGreaterThan(0); + }); + + it('Should handle a battle where one player is already dead', async () => { + const player1 = playerFabric.createRandomPlayer(); + const player2 = playerFabric.createRandomPlayer(); + player2.takeDamage(player2.health, player1); + const winner = await game.battle([player1, player2]); + expect(winner).toBe(player1); + }); + + it('Should handle a battle with skill usage', async () => { + const player1 = playerFabric.createRandomPlayer(); + const player2 = playerFabric.createRandomPlayer(); + await game.battle([player1, player2]); + expect(logger.skillLogs.length).toBeGreaterThanOrEqual(0); + }); + + it('Should correctly update the players array after a battle', async () => { + const player1 = playerFabric.createRandomPlayer(); + const player2 = playerFabric.createRandomPlayer(); + const winner = await game.battle([player1, player2]); + expect(winner.isAlive).toBe(true); + expect(winner.health).toBeGreaterThanOrEqual(1); + }); + + it('Should handle a game with a single player', async () => { + game = new Game(1, undefined, logger); + await game.start(); + expect(logger.messages.length).toBeGreaterThan(0); + expect(logger.messages[0]).toBe('Игра началась!'); + expect(logger.messages[1]).toContain('Список участников'); + expect(logger.messages[2]).toContain(`Победитель`); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/ISkill.spec.ts b/rpgsaga/saga/tests/sagaTests/ISkill.spec.ts new file mode 100644 index 000000000..bd8e7581d --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/ISkill.spec.ts @@ -0,0 +1,89 @@ +import { Player } from '../../src/game/abstract/Player'; +import { ISkill } from '../../src/game/skills/ISkill'; + +describe('ISkill interface tests', () => { + it('Should create a valid skill object', () => { + const skill: ISkill = { + name: 'Test Skill', + isUsed: false, + usageCount: 3, + initialSkillUsage: 3, + effect: (caster, opponent) => {}, + }; + expect(skill.name).toBe('Test Skill'); + expect(skill.isUsed).toBe(false); + expect(skill.usageCount).toBe(3); + expect(skill.initialSkillUsage).toBe(3); + expect(skill.effect).toBeDefined(); + }); + + it('Should create a valid skill object with damage function', () => { + const skill: ISkill = { + name: 'Damage Skill', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + damage: (caster: Player) => { + return caster.strength * 2; + }, + }; + expect(skill.name).toBe('Damage Skill'); + expect(skill.isUsed).toBe(false); + expect(skill.usageCount).toBe(1); + expect(skill.initialSkillUsage).toBe(1); + expect(skill.damage).toBeDefined(); + }); + + it('Should create a valid skill object with turns', () => { + const skill: ISkill = { + name: 'Timed Skill', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + turns: 3, + initialTurns: 3, + }; + expect(skill.name).toBe('Timed Skill'); + expect(skill.isUsed).toBe(false); + expect(skill.usageCount).toBe(1); + expect(skill.initialSkillUsage).toBe(1); + expect(skill.turns).toBe(3); + expect(skill.initialTurns).toBe(3); + }); + + it('Should create a valid skill object with buff', () => { + const skill: ISkill = { + name: 'Buff Skill', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + buff: { + strength: 5, + }, + }; + expect(skill.name).toBe('Buff Skill'); + expect(skill.isUsed).toBe(false); + expect(skill.usageCount).toBe(1); + expect(skill.initialSkillUsage).toBe(1); + expect(skill.buff).toBeDefined(); + expect(skill.buff!.strength).toBe(5); + }); + + it('Should handle a skill with optional properties', () => { + const skill: ISkill = { + name: 'Basic Skill', + isUsed: false, + usageCount: 1, + initialSkillUsage: 1, + }; + expect(skill.name).toBe('Basic Skill'); + expect(skill.isUsed).toBe(false); + expect(skill.usageCount).toBe(1); + expect(skill.initialSkillUsage).toBe(1); + expect(skill.damage).toBeUndefined(); + expect(skill.effect).toBeUndefined(); + expect(skill.buff).toBeUndefined(); + expect(skill.turns).toBeUndefined(); + expect(skill.initialTurns).toBeUndefined(); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts index d356e9240..4bd45400b 100644 --- a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -1,53 +1,154 @@ -import { Knight } from '../../src/classes/Knight'; +import { Archer, Knight, Wizard } from '../../src/game/classes'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; +import { WeaponFabric } from '../../src/game/fabrics/weaponsFabric/WeaponFabric'; describe('Knight class methods tests', () => { it('Constructor test', () => { - let newKnight = new Knight(75, 25, 'Ibragim'); - expect(newKnight.healthPoints).toEqual(75); - expect(newKnight.strengthPoints).toBe(25); - expect(newKnight.playerName).toBe('Ibragim'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newKnight = new Knight(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('sword'), [ + skillFabric.createSkillFromTemplate('удар возмездия')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + expect(newKnight).toBeInstanceOf(Knight); + expect(newKnight.health).toBe(75); + expect(newKnight.strength).toBe(25); + expect(newKnight.name).toBe('Ibragim'); }); + describe('Get methods tests', () => { - let newKnight = new Knight(75, 25, 'Ibragim'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newKnight = new Knight(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('sword'), [ + skillFabric.createSkillFromTemplate('удар возмездия')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + it('Health get test', () => { - expect(newKnight.healthPoints).toEqual(75); + expect(newKnight.health).toBe(75); }); it('Strength get test', () => { - expect(newKnight.strengthPoints).toBe(25); + expect(newKnight.strength).toBe(25); }); it('Name get test', () => { - expect(newKnight.playerName).toBe('Ibragim'); + expect(newKnight.name).toBe('Ibragim'); }); - }); - describe('Knight methods tests', () => { - let newKnight = new Knight(75, 25, 'Ibragim'); - let opponent = new Knight(86, 26, 'Mustafa'); - it('Should change the propertie "skillUsed" to true', () => { - newKnight.useSkill(opponent); - expect(newKnight.playerSkillUsed).toEqual(true); + it('ClassName get test', () => { + expect(newKnight.className).toBe('Knight'); }); - it('Should return health after using a skill', () => { - expect(opponent.healthPoints).toEqual(86 - newKnight.strengthPoints * 1.3); + it('IsAlive get test', () => { + expect(newKnight.isAlive).toBe(true); }); - it('Should return health after an attack using a skill', () => { - newKnight.attack(opponent); - expect(opponent.healthPoints).toEqual(86 - (newKnight.strengthPoints * 1.3 + newKnight.strengthPoints)); + it('IsSkillUsed get test', () => { + expect(newKnight.isSkillUsed).toBe(false); + }); + it('IsSkillUsed get test after using skill', () => { + newKnight.choseSkill(); + newKnight.useSkill(newKnight); + expect(newKnight.isSkillUsed).toBe(true); + }); + it('InitialHealth get test', () => { + expect(newKnight.initialHealth).toBe(75); + }); + it('InitialStrength get test', () => { + expect(newKnight.initialStrength).toBe(25); + }); + it('CountOfSkippingTurns get test', () => { + expect(newKnight.countOfSkipingTurns).toBe(0); + }); + it('CountOfSkippingTurns get test after using skipping spell', () => { + const opponent = new Wizard(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + opponent.useSkill(newKnight, 'заворожение'); + expect(newKnight.countOfSkipingTurns).toBe(1); }); }); + describe('Knight methods tests', () => { - let newKnight = new Knight(75, 25, 'Ibragim'); - let opponent = new Knight(86, 26, 'Mustafa'); + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newKnight = new Knight(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('sword'), [ + skillFabric.createSkillFromTemplate('удар возмездия')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + const opponent = new Archer(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('bow'), [ + skillFabric.createSkillFromTemplate('огненные стрелы')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + it('Should return health after an attack whithout using a skill', () => { newKnight.attack(opponent); - expect(opponent.healthPoints).toEqual(86 - newKnight.strengthPoints); + expect(opponent.health).toBe(86 - (newKnight.strength + newKnight.weapon.damage)); }); + it('Health should decrease by the number of damage units', () => { - newKnight.takeDamage(45); - expect(newKnight.healthPoints).toEqual(75 - 45); + newKnight.takeDamage(45, opponent, opponent.currentSkill); + expect(newKnight.health).toBe(75 - 45); + }); + + it('Health should decrease by the number of damage units without skill buff', () => { + newKnight.heal(100); + opponent.useSkill(newKnight, 'ледяные стрелы'); + opponent.attack(newKnight); + expect(newKnight.health).toBe( + 75 - (opponent.strength - opponent.currentSkill!.buff!.strength + opponent.weapon.damage), + ); + }); + + it('Strength should icnrease', () => { + newKnight.damageUp(2); + expect(newKnight.strength).toBe(27); }); + + it('Should change the propertie "skillUsed" to true', () => { + newKnight.choseSkill(); + newKnight.useSkill(opponent); + expect(newKnight.isSkillUsed).toBe(true); + }); + + it('Health should icnrease', () => { + newKnight.heal(10); + expect(newKnight.health).toBe( + 75 - (opponent.strength - opponent.currentSkill!.buff!.strength + opponent.weapon.damage) + 10, + ); + }); + + it('Health should be equal initialHealth', () => { + newKnight.heal(100); + expect(newKnight.health).toBe(newKnight.initialHealth); + }); + it('Ibragim should DIE.', () => { - newKnight.takeDamage(45); - expect(newKnight.isAlivePlayer).toEqual(false); + newKnight.takeDamage(newKnight.initialHealth, opponent); + expect(newKnight.isAlive).toBe(false); + expect(newKnight.health).toBe(0); + }); + + it('Ibragim health should be equal 0.', () => { + newKnight.takeDamage(1000, opponent, opponent.currentSkill); + expect(newKnight.health).toBe(0); + }); + + it('Ibragim should reset.', () => { + newKnight.reset(); + expect(newKnight.health).toBe(newKnight.initialHealth); + expect(newKnight.strength).toBe(newKnight.initialStrength); + expect(newKnight.isSkillUsed).toBe(false); + newKnight.skills!.forEach(skill => { + expect(skill.usageCount).toBe(skill.initialSkillUsage); + expect(skill.isUsed).toBe(false); + expect(skill.turns).toBe(skill.initialTurns); + }); + }); + + it('Ibragim strength should be equal initialStrength.', () => { + newKnight.useSkill(opponent, 'ледяные стрелы'); + newKnight.attack(opponent); + newKnight.attack(opponent); + newKnight.attack(opponent); + expect(newKnight.strength).toBe(25); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/KnightFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/KnightFabric.spec.ts new file mode 100644 index 000000000..da8277425 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/KnightFabric.spec.ts @@ -0,0 +1,71 @@ +import { KnightFabric } from '../../src/game/fabrics/playersFabrics'; +import { Knight } from '../../src/game/classes'; +import { IWeapon } from '../../src/game/weapon/IWeapon'; +import { ISkill } from '../../src/game/skills/ISkill'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; + +class MockWeapon implements IWeapon { + name: string = 'Mock Weapon'; + typeOfDamage: string = 'Mock type'; + damage: number = 10; +} + +class MockSkill implements ISkill { + name: string = 'Mock Skill'; + description: string = 'Mock description'; + isUsed: boolean = false; + usageCount: number = 0; + initialSkillUsage: number = 0; + effect(): void {} +} + +class MockSkillFabric extends SkillFabric { + createSkillFromTemplate(templateName: string): ISkill | null { + if (templateName === 'ледяные стрелы') { + return new MockSkill(); + } else if (templateName === 'удар возмездия') { + return new MockSkill(); + } + return null; + } +} + +describe('KnightFabric tests', () => { + let knightFabric: KnightFabric; + let mockSkillFabric: MockSkillFabric; + let mockWeapon: MockWeapon; + + beforeEach(() => { + mockSkillFabric = new MockSkillFabric(); + knightFabric = new KnightFabric(); + mockWeapon = new MockWeapon(); + (knightFabric as any).skillFabric = mockSkillFabric; + }); + + it('Should create a knight with provided skills', () => { + const mockSkills: ISkill[] = [new MockSkill(), new MockSkill()]; + const newKnight = knightFabric.createKnight(['Alice', 'Bob'], 100, 20, mockWeapon, mockSkills); + expect(newKnight).toBeInstanceOf(Knight); + expect(newKnight.health).toBe(100); + expect(newKnight.strength).toBe(20); + expect(newKnight.weapon).toBe(mockWeapon); + expect(newKnight.skills).toBe(mockSkills); + }); + + it('Should create an knight with default skills if no skills are provided', () => { + const newKnight = knightFabric.createKnight(['Alice', 'Bob'], 100, 20, mockWeapon); + expect(newKnight).toBeInstanceOf(Knight); + expect(newKnight.health).toBe(100); + expect(newKnight.strength).toBe(20); + expect(newKnight.weapon).toBe(mockWeapon); + expect(newKnight.skills.length).toBe(2); + }); + + it('Should select a random name from the provided names array', () => { + const names = ['Alice', 'Bob', 'Charlie']; + const newKnight1 = knightFabric.createKnight(names, 100, 20, mockWeapon); + const newKnight2 = knightFabric.createKnight(names, 100, 20, mockWeapon); + expect(names.includes(newKnight1.name)).toBe(true); + expect(names.includes(newKnight2.name)).toBe(true); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/Logger.spec.ts b/rpgsaga/saga/tests/sagaTests/Logger.spec.ts deleted file mode 100644 index da7abbfb7..000000000 --- a/rpgsaga/saga/tests/sagaTests/Logger.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Logger } from '../../src/utils/output/Logger'; - -describe('Logger class methods tests', () => { - it('method log test', () => { - expect(Logger.log('Игра началась!')).toEqual(console.log('Игра началась!')); - }); -}); diff --git a/rpgsaga/saga/tests/sagaTests/PlayerFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/PlayerFabric.spec.ts new file mode 100644 index 000000000..e2f181efc --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/PlayerFabric.spec.ts @@ -0,0 +1,122 @@ +import { PlayerFabric } from '../../src/game/fabrics/playersFabrics'; +import { Player } from '../../src/game/abstract/Player'; +import { IWeapon } from '../../src/game/weapon/IWeapon'; +import { ISkill } from '../../src/game/skills/ISkill'; +import { ArcherFabric } from '../../src/game/fabrics/playersFabrics/ArcherFabric'; +import { KnightFabric } from '../../src/game/fabrics/playersFabrics/KnightFabric'; +import { WizardFabric } from '../../src/game/fabrics/playersFabrics/WizardFabric'; + +class MockWeapon { + name: string; + typeOfDamage: string; + damage: number; + constructor(name: string, typeOfDamage: string, damage: number) { + this.name = name; + this.typeOfDamage = typeOfDamage; + this.damage = damage; + } +} + +class MockArcherFabric extends ArcherFabric { + createArcher( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + return new MockPlayer('Archer', playerHealth, playerStrength, playerWeapon, playerSkills); + } +} + +class MockKnightFabric extends KnightFabric { + createKnight( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + return new MockPlayer('Knight', playerHealth, playerStrength, playerWeapon, playerSkills); + } +} + +class MockWizardFabric extends WizardFabric { + createWizard( + names: string[], + playerHealth: number, + playerStrength: number, + playerWeapon: IWeapon, + playerSkills: ISkill[] | null = null, + ): Player { + return new MockPlayer('Wizard', playerHealth, playerStrength, playerWeapon, playerSkills); + } +} + +class MockPlayer extends Player { + constructor(className: string, health: number, strength: number, weapon: IWeapon, skills: ISkill[] | null = null) { + super(health, strength, '', weapon, skills!); + this._className = className; + } +} + +class MockWeaponFabric { + createRandomWeapon(type: string, damageType: string): MockWeapon { + return new MockWeapon(type, damageType, 10); + } +} + +describe('PlayerFabric tests', () => { + let playerFabric: PlayerFabric; + let mockWeaponFabric: MockWeaponFabric; + let mockArcherFabric: MockArcherFabric; + let mockKnightFabric: MockKnightFabric; + let mockWizardFabric: MockWizardFabric; + + beforeEach(() => { + mockWeaponFabric = new MockWeaponFabric(); + mockArcherFabric = new MockArcherFabric(); + mockKnightFabric = new MockKnightFabric(); + mockWizardFabric = new MockWizardFabric(); + playerFabric = new PlayerFabric(); + (playerFabric as any).weaponFabric = mockWeaponFabric; + (playerFabric as any).archerFabric = mockArcherFabric; + (playerFabric as any).knightFabric = mockKnightFabric; + (playerFabric as any).wizardFabric = mockWizardFabric; + }); + + it('Should create a Knight', () => { + const player = playerFabric.createPlayer('Knight', 100, 20, new MockWeapon('Mock Weapon', 'Mock Type', 10)); + expect(player).toBeInstanceOf(Player); + expect(player?.className).toBe('Knight'); + }); + + it('Should create an Archer', () => { + const player = playerFabric.createPlayer('Archer', 100, 20, new MockWeapon('Mock Weapon', 'Mock Type', 10)); + expect(player).toBeInstanceOf(Player); + expect(player?.className).toBe('Archer'); + }); + + it('Should create a Wizard', () => { + const player = playerFabric.createPlayer('Wizard', 100, 20, new MockWeapon('Mock Weapon', 'Mock Type', 10)); + expect(player).toBeInstanceOf(Player); + expect(player?.className).toBe('Wizard'); + }); + + it('Should return undefined for an invalid class', () => { + const player = playerFabric.createPlayer('InvalidClass', 100, 20, new MockWeapon('Mock Weapon', 'Mock Type', 10)); + expect(player).toBeUndefined(); + }); + + it('Should create a random player', () => { + const player = playerFabric.createRandomPlayer(); + expect(player).toBeInstanceOf(Player); + expect(['Knight', 'Archer', 'Wizard']).toContain(player.className); + }); + + it('Should create random players', () => { + const players = playerFabric.createRandomPlayers(3); + expect(players.length).toBe(3); + players.forEach(player => expect(['Knight', 'Archer', 'Wizard']).toContain(player.className)); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts new file mode 100644 index 000000000..8c36b2fc5 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts @@ -0,0 +1,76 @@ +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; +import { Archer } from '../../src/game/classes'; +import { WeaponFabric } from '../../src/game/fabrics/weaponsFabric/WeaponFabric'; + +describe('SkillFabric tests', () => { + const skillFabric = new SkillFabric(); + const weaponFabric = new WeaponFabric(); + + it('Should create a skill from template', () => { + const skill = skillFabric.createSkillFromTemplate('огненные стрелы'); + expect(skill).toBeDefined(); + expect(skill?.name).toBe('огненные стрелы'); + expect(skill?.usageCount).toBe(1); + expect(skill?.initialSkillUsage).toBe(1); + expect(skill?.effect).toBeDefined(); + }); + + it('Should create a skill from template with turns', () => { + const skill = skillFabric.createSkillFromTemplate('ледяные стрелы'); + expect(skill).toBeDefined(); + expect(skill?.name).toBe('ледяные стрелы'); + expect(skill?.turns).toBe(3); + expect(skill?.initialTurns).toBe(3); + }); + + it('Should create a skill from template with damage calculation', () => { + const skill = skillFabric.createSkillFromTemplate('удар возмездия'); + expect(skill).toBeDefined(); + expect(skill?.name).toBe('удар возмездия'); + expect(skill?.damage).toBeDefined(); + + const player = new Archer(100, 20, '', weaponFabric.createRandomWeapon('bow'), []); + const opponent = new Archer(100, 10, '', weaponFabric.createRandomWeapon('bow'), []); + + skill?.effect!(player, opponent); + expect(opponent.health).toBe(100 - (20 * 1.3 + player.weapon.damage)); + }); + + it('Should create a skill from template with skip turns effect', () => { + const skill = skillFabric.createSkillFromTemplate('заворожение'); + expect(skill).toBeDefined(); + expect(skill?.name).toBe('заворожение'); + + const player = new Archer(100, 20, '', weaponFabric.createRandomWeapon('bow'), []); + const opponent = new Archer(100, 10, '', weaponFabric.createRandomWeapon('bow'), []); + + skill?.effect!(player, opponent); + expect(opponent.countOfSkipingTurns).toBe(1); + }); + + it('Should return null for an invalid template name', () => { + const skill = skillFabric.createSkillFromTemplate('invalidSkillName'); + expect(skill).toBeNull(); + }); + + it('Should correctly apply skill effects', () => { + const player = new Archer(100, 20, '', weaponFabric.createRandomWeapon('bow'), []); + const opponent = new Archer(100, 10, '', weaponFabric.createRandomWeapon('bow'), []); + + const fireArrows = skillFabric.createSkillFromTemplate('огненные стрелы')!; + fireArrows.effect!(player, opponent); + expect(player.strength).toBe(22); + + const iceArrows = skillFabric.createSkillFromTemplate('ледяные стрелы')!; + iceArrows.effect!(player, opponent); + expect(player.strength).toBe(25); + + const vengeanceStrike = skillFabric.createSkillFromTemplate('удар возмездия')!; + vengeanceStrike.effect!(player, opponent); + expect(opponent.health).toBeLessThan(100); + + const enchantment = skillFabric.createSkillFromTemplate('заворожение')!; + enchantment.effect!(player, opponent); + expect(opponent.health).toBeLessThan(100); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts deleted file mode 100644 index e1a62f79c..000000000 --- a/rpgsaga/saga/tests/sagaTests/Vizard.spec.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { Vizard } from '../../src/classes/Vizard'; - -describe('Vizard class methods tests', () => { - it('Constructor test', () => { - let newVizard = new Vizard(75, 25, 'Ibragim'); - expect(newVizard.healthPoints).toEqual(75); - expect(newVizard.strengthPoints).toBe(25); - expect(newVizard.playerName).toBe('Ibragim'); - }); - describe('Get methods tests', () => { - let newVizard = new Vizard(75, 25, 'Ibragim'); - it('Health get test', () => { - expect(newVizard.healthPoints).toEqual(75); - }); - it('Strength get test', () => { - expect(newVizard.strengthPoints).toBe(25); - }); - it('Name get test', () => { - expect(newVizard.playerName).toBe('Ibragim'); - }); - }); - describe('Vizard methods tests', () => { - let newVizard = new Vizard(75, 25, 'Ibragim'); - let opponent = new Vizard(86, 26, 'Mustafa'); - it('Should change the propertie "skillUsed" to true', () => { - newVizard.useSkill(opponent); - expect(newVizard.playerSkillUsed).toEqual(true); - }); - it('Should return health after an attack', () => { - newVizard.attack(opponent); - expect(opponent.healthPoints).toEqual(86 - newVizard.strengthPoints); - }); - it('Should change the propertie "skillUsed" to false', () => { - newVizard.takeDamage(55); - expect(newVizard.playerSkillUsed).toEqual(false); - }); - }); - describe('Vizard methods tests', () => { - let newVizard = new Vizard(75, 25, 'Ibragim'); - it('Health should decrease by the number of damage units', () => { - newVizard.takeDamage(45); - expect(newVizard.healthPoints).toEqual(75 - 45); - }); - it('Ibragim should DIE.', () => { - newVizard.takeDamage(45); - expect(newVizard.isAlivePlayer).toEqual(false); - }); - }); -}); diff --git a/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts new file mode 100644 index 000000000..a2a6f040d --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts @@ -0,0 +1,70 @@ +import { WeaponFabric } from '../../src/game/fabrics/weaponsFabric/WeaponFabric'; + +describe('WeaponFabric', () => { + const weaponFabric = new WeaponFabric(); + + it('should create a sword', () => { + const weapon = weaponFabric.createWeapon('sword', 'огонь', 'Dragonsbane', 10); + expect(weapon.name).toBe('Dragonsbane'); + expect(weapon.typeOfDamage).toBe('огонь'); + expect(weapon.damage).toBe(10); + }); + + it('should create a stick', () => { + const weapon = weaponFabric.createWeapon('stick', 'яд', 'Oak Staff', 5); + expect(weapon.name).toBe('Oak Staff'); + expect(weapon.typeOfDamage).toBe('яд'); + expect(weapon.damage).toBe(5); + }); + + it('should create a bow', () => { + const weapon = weaponFabric.createWeapon('bow', 'лёд', 'Longbow', 8); + expect(weapon.name).toBe('Longbow'); + expect(weapon.typeOfDamage).toBe('лёд'); + expect(weapon.damage).toBe(8); + }); + + it('should create fists as default', () => { + const weapon = weaponFabric.createWeapon('invalidType', 'invalidDamageType', 'invalidName', 0); + expect(weapon.name).toBe('fists'); + expect(weapon.typeOfDamage).toBe('-'); + expect(weapon.damage).toBe(3); + }); + + it('should create a random sword', () => { + const weapon = weaponFabric.createRandomWeapon('sword'); + expect(weapon.name).toBeDefined(); + expect(weapon.typeOfDamage).toBeDefined(); + expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeLessThanOrEqual(10); + expect(['Dragonsbane', 'Stormbringer', 'Aethelred']).toContain(weapon.name); + expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); + }); + + it('should create a random stick', () => { + const weapon = weaponFabric.createRandomWeapon('stick'); + expect(weapon.name).toBeDefined(); + expect(weapon.typeOfDamage).toBeDefined(); + expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeLessThanOrEqual(10); + expect(['Oak Staff', 'Elderwood Branch', "Shepherd's Crook"]).toContain(weapon.name); + expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); + }); + + it('should create a random bow', () => { + const weapon = weaponFabric.createRandomWeapon('bow'); + expect(weapon.name).toBeDefined(); + expect(weapon.typeOfDamage).toBeDefined(); + expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeLessThanOrEqual(10); + expect(["Hunter's Bow", 'Longbow', 'Shortbow']).toContain(weapon.name); + expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); + }); + + it('should handle invalid weapon type in createRandomWeapon', () => { + const weapon = weaponFabric.createRandomWeapon('invalidType'); + expect(weapon.name).toBe('fists'); + expect(weapon.typeOfDamage).toBe('-'); + expect(weapon.damage).toBe(3); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts new file mode 100644 index 000000000..54155eaf4 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts @@ -0,0 +1,142 @@ +import { Wizard } from '../../src/game/classes'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; +import { WeaponFabric } from '../../src/game/fabrics/weaponsFabric/WeaponFabric'; + +describe('Wizard class methods tests', () => { + it('Constructor test', () => { + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newWizard = new Wizard(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + expect(newWizard).toBeInstanceOf(Wizard); + expect(newWizard.health).toBe(75); + expect(newWizard.strength).toBe(25); + expect(newWizard.name).toBe('Ibragim'); + }); + + describe('Get methods tests', () => { + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newWizard = new Wizard(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + + it('Health get test', () => { + expect(newWizard.health).toBe(75); + }); + it('Strength get test', () => { + expect(newWizard.strength).toBe(25); + }); + it('Name get test', () => { + expect(newWizard.name).toBe('Ibragim'); + }); + it('ClassName get test', () => { + expect(newWizard.className).toBe('Wizard'); + }); + it('IsAlive get test', () => { + expect(newWizard.isAlive).toBe(true); + }); + it('IsSkillUsed get test', () => { + expect(newWizard.isSkillUsed).toBe(false); + }); + it('IsSkillUsed get test after using skill', () => { + newWizard.useSkill(newWizard, 'ледяные стрелы'); + expect(newWizard.isSkillUsed).toBe(true); + }); + it('InitialHealth get test', () => { + expect(newWizard.initialHealth).toBe(75); + }); + it('InitialStrength get test', () => { + expect(newWizard.initialStrength).toBe(25); + }); + it('CountOfSkippingTurns get test', () => { + expect(newWizard.countOfSkipingTurns).toBe(0); + }); + it('CountOfSkippingTurns get test after using skipping spell', () => { + const opponent = new Wizard(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + opponent.useSkill(newWizard, 'заворожение'); + expect(newWizard.countOfSkipingTurns).toBe(1); + }); + }); + + describe('Wizard methods tests', () => { + const weaponFabric = new WeaponFabric(); + const skillFabric = new SkillFabric(); + const newWizard = new Wizard(75, 25, 'Ibragim', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + const opponent = new Wizard(86, 26, 'Mustafa', weaponFabric.createRandomWeapon('stick'), [ + skillFabric.createSkillFromTemplate('заворожение')!, + skillFabric.createSkillFromTemplate('ледяные стрелы')!, + ]); + + it('Should return health after an attack whithout using a skill', () => { + newWizard.attack(opponent); + expect(opponent.health).toBe(86 - (newWizard.strength + newWizard.weapon.damage)); + }); + + it('Health should decrease by the number of damage units', () => { + newWizard.takeDamage(45, opponent, opponent.currentSkill); + expect(newWizard.health).toBe(75 - 45); + }); + + it('Strength should icnrease', () => { + newWizard.damageUp(2); + expect(newWizard.strength).toBe(27); + }); + + it('Should change the propertie "skillUsed" to true', () => { + newWizard.choseSkill(); + newWizard.useSkill(opponent); + expect(newWizard.isSkillUsed).toBe(true); + }); + + it('Health should icnrease', () => { + newWizard.heal(10); + expect(newWizard.health).toBe(40); + }); + + it('Health should be equal initialHealth', () => { + newWizard.heal(100); + expect(newWizard.health).toBe(newWizard.initialHealth); + }); + + it('Ibragim should DIE.', () => { + newWizard.takeDamage(newWizard.initialHealth, opponent, opponent.currentSkill); + expect(newWizard.isAlive).toBe(false); + expect(newWizard.health).toBe(0); + }); + + it('Ibragim health should be equal 0.', () => { + newWizard.takeDamage(1000, opponent, opponent.currentSkill); + expect(newWizard.health).toBe(0); + }); + + it('Ibragim should reset.', () => { + newWizard.reset(); + expect(newWizard.health).toBe(newWizard.initialHealth); + expect(newWizard.strength).toBe(newWizard.initialStrength); + expect(newWizard.isSkillUsed).toBe(false); + newWizard.skills!.forEach(skill => { + expect(skill.usageCount).toBe(skill.initialSkillUsage); + expect(skill.isUsed).toBe(false); + expect(skill.turns).toBe(skill.initialTurns); + }); + }); + + it('Ibragim strength should be equal initialStrength.', () => { + newWizard.useSkill(opponent, 'ледяные стрелы'); + newWizard.attack(opponent); + newWizard.attack(opponent); + newWizard.attack(opponent); + expect(newWizard.strength).toBe(25); + }); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/WizardFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/WizardFabric.spec.ts new file mode 100644 index 000000000..a268d2e90 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/WizardFabric.spec.ts @@ -0,0 +1,71 @@ +import { WizardFabric } from '../../src/game/fabrics/playersFabrics'; +import { Wizard } from '../../src/game/classes'; +import { IWeapon } from '../../src/game/weapon/IWeapon'; +import { ISkill } from '../../src/game/skills/ISkill'; +import { SkillFabric } from '../../src/game/fabrics/skillFabric/SkillFabric'; + +class MockWeapon implements IWeapon { + name: string = 'Mock Weapon'; + typeOfDamage: string = 'Mock type'; + damage: number = 10; +} + +class MockSkill implements ISkill { + name: string = 'Mock Skill'; + description: string = 'Mock description'; + isUsed: boolean = false; + usageCount: number = 0; + initialSkillUsage: number = 0; + effect(): void {} +} + +class MockSkillFabric extends SkillFabric { + createSkillFromTemplate(templateName: string): ISkill | null { + if (templateName === 'ледяные стрелы') { + return new MockSkill(); + } else if (templateName === 'заворожение') { + return new MockSkill(); + } + return null; + } +} + +describe('WizardFabric tests', () => { + let wizardFabric: WizardFabric; + let mockSkillFabric: MockSkillFabric; + let mockWeapon: MockWeapon; + + beforeEach(() => { + mockSkillFabric = new MockSkillFabric(); + wizardFabric = new WizardFabric(); + mockWeapon = new MockWeapon(); + (wizardFabric as any).skillFabric = mockSkillFabric; + }); + + it('Should create a wizard with provided skills', () => { + const mockSkills: ISkill[] = [new MockSkill(), new MockSkill()]; + const newWizard = wizardFabric.createWizard(['Alice', 'Bob'], 100, 20, mockWeapon, mockSkills); + expect(newWizard).toBeInstanceOf(Wizard); + expect(newWizard.health).toBe(100); + expect(newWizard.strength).toBe(20); + expect(newWizard.weapon).toBe(mockWeapon); + expect(newWizard.skills).toBe(mockSkills); + }); + + it('Should create an wizard with default skills if no skills are provided', () => { + const newWizard = wizardFabric.createWizard(['Alice', 'Bob'], 100, 20, mockWeapon); + expect(newWizard).toBeInstanceOf(Wizard); + expect(newWizard.health).toBe(100); + expect(newWizard.strength).toBe(20); + expect(newWizard.weapon).toBe(mockWeapon); + expect(newWizard.skills.length).toBe(2); + }); + + it('Should select a random name from the provided names array', () => { + const names = ['Alice', 'Bob', 'Charlie']; + const newWizard1 = wizardFabric.createWizard(names, 100, 20, mockWeapon); + const newWizard2 = wizardFabric.createWizard(names, 100, 20, mockWeapon); + expect(names.includes(newWizard1.name)).toBe(true); + expect(names.includes(newWizard2.name)).toBe(true); + }); +}); From 73f2a60ce928ed2a304baf4b8f62212bd1a79e8f Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 17:23:43 +0300 Subject: [PATCH 17/27] lint fixes --- .../game/fabrics/playersFabrics/PlayerFabric.ts | 1 + .../src/game/fabrics/skillFabric/SkillFabric.ts | 4 ++-- .../saga/src/game/utils/input/createCharacter.ts | 2 +- rpgsaga/saga/src/game/utils/input/createGame.ts | 1 + rpgsaga/saga/src/game/utils/output/Logger.ts | 14 +++++++------- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts b/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts index c57d1aa54..dd9e92f29 100644 --- a/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts +++ b/rpgsaga/saga/src/game/fabrics/playersFabrics/PlayerFabric.ts @@ -3,6 +3,7 @@ import { ISkill } from '../../skills/ISkill'; import { getRandomArrayElement, getRandomNumber } from '../../utils/randomization'; import { IWeapon } from '../../weapon/IWeapon'; import { WeaponFabric } from '../weaponsFabric/WeaponFabric'; + import { ArcherFabric } from './ArcherFabric'; import { KnightFabric } from './KnightFabric'; import { WizardFabric } from './WizardFabric'; diff --git a/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts index 72781e238..f9398a6eb 100644 --- a/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts +++ b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts @@ -8,7 +8,7 @@ export class SkillFabric { isUsed: false, usageCount: 1, initialSkillUsage: 1, - effect: (caster: Player, opponent: Player) => { + effect: (caster: Player) => { caster.damageUp(2); }, buff: { @@ -22,7 +22,7 @@ export class SkillFabric { initialSkillUsage: 1, turns: 3, initialTurns: 3, - effect: (caster: Player, opponent: Player) => { + effect: (caster: Player) => { caster.damageUp(3); }, buff: { diff --git a/rpgsaga/saga/src/game/utils/input/createCharacter.ts b/rpgsaga/saga/src/game/utils/input/createCharacter.ts index 5965745e7..236999fc3 100644 --- a/rpgsaga/saga/src/game/utils/input/createCharacter.ts +++ b/rpgsaga/saga/src/game/utils/input/createCharacter.ts @@ -16,8 +16,8 @@ export async function createCharacter(numberOfPlayers: number): Promise { let playerHealth: number = 0; let playerStrength: number = 0; let playerWeapon: IWeapon; - let playerSkills: ISkill[] = []; + const playerSkills: ISkill[] = []; const playerFabric = new PlayerFabric(); const types: string[] = ['Knight', 'Archer', 'Wizard']; const weapons: string[] = ['bow', 'sword', 'stick']; diff --git a/rpgsaga/saga/src/game/utils/input/createGame.ts b/rpgsaga/saga/src/game/utils/input/createGame.ts index d13fb00b6..f8fc745de 100644 --- a/rpgsaga/saga/src/game/utils/input/createGame.ts +++ b/rpgsaga/saga/src/game/utils/input/createGame.ts @@ -1,6 +1,7 @@ import { Game } from '../../gameplay/Game'; import { Logger } from '../output/Logger'; import { readAnswer } from '../question/readAnswer'; + import { createCharacter } from './createCharacter'; export function createGame(): void { diff --git a/rpgsaga/saga/src/game/utils/output/Logger.ts b/rpgsaga/saga/src/game/utils/output/Logger.ts index e61523297..8ac29d392 100644 --- a/rpgsaga/saga/src/game/utils/output/Logger.ts +++ b/rpgsaga/saga/src/game/utils/output/Logger.ts @@ -1,16 +1,16 @@ import { Player } from '../../abstract/Player'; export class Logger { - //static _instance: Logger; + // static _instance: Logger; constructor() {} - //public get instance(): Logger { - // if (!Logger._instance) { - // Logger._instance = new Logger(); - // } - // return Logger._instance; - //} + // public get instance(): Logger { + // if (!Logger._instance) { + // Logger._instance = new Logger(); + // } + // return Logger._instance; + // } public messageLog(message: string): void { const timestamp: string = new Date().toISOString(); From d96b9a7b7ad444e1a892d9292e4fd3c48fb8a4e1 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 17:44:24 +0300 Subject: [PATCH 18/27] lint fixes --- rpgsaga/saga/src/game/abstract/Player.ts | 2 ++ rpgsaga/saga/src/game/classes/Archer.ts | 2 +- rpgsaga/saga/src/game/classes/Wizard.ts | 2 +- rpgsaga/saga/src/game/utils/input/createGame.ts | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index f425c389d..5de87dd1f 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -150,6 +150,7 @@ export abstract class Player { this._strength += buff; } + /* eslint-disable no-unused-vars */ public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { this._health -= damage; if (this._health <= 0) { @@ -157,6 +158,7 @@ export abstract class Player { this._isAlive = false; } } + /* eslint-enable no-unused-vars */ public heal(amount: number) { if (this._health + amount > this.initialHealth) { diff --git a/rpgsaga/saga/src/game/classes/Archer.ts b/rpgsaga/saga/src/game/classes/Archer.ts index 7ad6c17b1..15462c113 100644 --- a/rpgsaga/saga/src/game/classes/Archer.ts +++ b/rpgsaga/saga/src/game/classes/Archer.ts @@ -3,7 +3,7 @@ import { ISkill } from '../skills/ISkill'; import { IWeapon } from '../weapon/IWeapon'; export class Archer extends Player { - public _className: string = 'Archer'; + protected _className: string = 'Archer'; constructor( playerHealth: number, diff --git a/rpgsaga/saga/src/game/classes/Wizard.ts b/rpgsaga/saga/src/game/classes/Wizard.ts index 56b6ea580..39cf6eb9f 100644 --- a/rpgsaga/saga/src/game/classes/Wizard.ts +++ b/rpgsaga/saga/src/game/classes/Wizard.ts @@ -3,7 +3,7 @@ import { ISkill } from '../skills/ISkill'; import { IWeapon } from '../weapon/IWeapon'; export class Wizard extends Player { - public _className: string = 'Wizard'; + protected _className: string = 'Wizard'; constructor( playerHealth: number, diff --git a/rpgsaga/saga/src/game/utils/input/createGame.ts b/rpgsaga/saga/src/game/utils/input/createGame.ts index f8fc745de..6207cc112 100644 --- a/rpgsaga/saga/src/game/utils/input/createGame.ts +++ b/rpgsaga/saga/src/game/utils/input/createGame.ts @@ -21,12 +21,12 @@ export function createGame(): void { async function askForCreating() { const inputString: string = await readAnswer('Хотите ли вы создать своего персонажа? (да/нет) '); + const game = new Game(number, undefined, logger); switch (inputString.toLowerCase()) { case 'да': createCharacter(number); break; case 'нет': - const game = new Game(number, undefined, logger); await game.start(); break; default: From 9e4d779c77e3f427fff55c98954fc03f62b5cb04 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 17:49:38 +0300 Subject: [PATCH 19/27] lint fix 3 --- rpgsaga/saga/src/game/abstract/Player.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index 5de87dd1f..6f89402ee 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -150,15 +150,17 @@ export abstract class Player { this._strength += buff; } - /* eslint-disable no-unused-vars */ public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { + let opponentSkill: ISkill; + if (skill !== undefined) { + opponentSkill = skill; + } this._health -= damage; if (this._health <= 0) { this._health = 0; this._isAlive = false; } } - /* eslint-enable no-unused-vars */ public heal(amount: number) { if (this._health + amount > this.initialHealth) { From c97e8d3a542f8a1511d23c3cc79e43514bada721 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 18:05:12 +0300 Subject: [PATCH 20/27] takeDamage changes --- rpgsaga/saga/src/game/abstract/Player.ts | 22 +++++++++---------- rpgsaga/saga/src/game/classes/Knight.ts | 4 ++-- .../game/fabrics/skillFabric/SkillFabric.ts | 8 +------ rpgsaga/saga/src/game/gameplay/Game.ts | 2 +- rpgsaga/saga/tests/sagaTests/Archer.spec.ts | 13 ++++------- rpgsaga/saga/tests/sagaTests/Game.spec.ts | 2 +- rpgsaga/saga/tests/sagaTests/Knight.spec.ts | 11 +++------- .../saga/tests/sagaTests/SkillFabric.spec.ts | 13 ++++++----- rpgsaga/saga/tests/sagaTests/Wizard.spec.ts | 11 +++------- 9 files changed, 32 insertions(+), 54 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index 6f89402ee..18af2f233 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -100,7 +100,9 @@ export abstract class Player { } if (this._currentSkill !== undefined && this._currentSkill.usageCount > 0) { - this._currentSkill.effect!(this, opponent); + if (this._currentSkill.effect) { + this._currentSkill.effect(this, opponent); + } this._currentSkill.usageCount--; this.skills.forEach(skill => { if (skill.name === this._currentSkill!.name) { @@ -125,9 +127,9 @@ export abstract class Player { this._updateSkills(); } - opponent.takeDamage(this._strength + this._weapon.damage, this, this._currentSkill); + opponent.takeDamage(this._strength + this._weapon.damage, this._currentSkill); } else { - opponent.takeDamage(this._strength + this._weapon.damage, this); + opponent.takeDamage(this._strength + this._weapon.damage); } } @@ -146,16 +148,12 @@ export abstract class Player { } } - public damageUp(buff: number) { - this._strength += buff; - } - - public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { - let opponentSkill: ISkill; - if (skill !== undefined) { - opponentSkill = skill; + public takeDamage(damage: number, skill: ISkill | undefined = undefined): void { + let currentDamage: number = damage; + if (skill !== undefined && skill.buff) { + currentDamage += skill.buff.strength; } - this._health -= damage; + this._health -= currentDamage; if (this._health <= 0) { this._health = 0; this._isAlive = false; diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts index c3978dafd..fed1d5820 100644 --- a/rpgsaga/saga/src/game/classes/Knight.ts +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -15,10 +15,10 @@ export class Knight extends Player { super(playerHealth, playerStrength, playerName, playerWeapon, playerSkills); } - public takeDamage(damage: number, attacker: Player, skill: ISkill | undefined = undefined): void { + public takeDamage(damage: number, skill: ISkill | undefined = undefined): void { let currentDamage: number = damage; if (skill !== undefined && skill.name === 'ледяные стрелы') { - currentDamage -= skill.buff!.strength; + currentDamage = damage; } this._health -= currentDamage; if (this._health <= 0) { diff --git a/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts index f9398a6eb..d1e3c5e8b 100644 --- a/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts +++ b/rpgsaga/saga/src/game/fabrics/skillFabric/SkillFabric.ts @@ -8,9 +8,6 @@ export class SkillFabric { isUsed: false, usageCount: 1, initialSkillUsage: 1, - effect: (caster: Player) => { - caster.damageUp(2); - }, buff: { strength: 2, }, @@ -22,9 +19,6 @@ export class SkillFabric { initialSkillUsage: 1, turns: 3, initialTurns: 3, - effect: (caster: Player) => { - caster.damageUp(3); - }, buff: { strength: 3, }, @@ -37,7 +31,7 @@ export class SkillFabric { damage: (caster: Player) => caster.strength * 1.3 + caster.weapon.damage, effect: (caster: Player, opponent: Player) => { const weaponDamage = caster.weapon ? caster.weapon.damage : 0; - opponent.takeDamage(caster.strength * 1.3 + weaponDamage, caster); + opponent.takeDamage(caster.strength * 1.3 + weaponDamage); }, }, { diff --git a/rpgsaga/saga/src/game/gameplay/Game.ts b/rpgsaga/saga/src/game/gameplay/Game.ts index 448ae46cf..dc969d9c7 100644 --- a/rpgsaga/saga/src/game/gameplay/Game.ts +++ b/rpgsaga/saga/src/game/gameplay/Game.ts @@ -80,7 +80,7 @@ export class Game { } } - await this.delay(200); + await this.delay(2); turn++; } diff --git a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts index bdaa39e6e..195197144 100644 --- a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -84,15 +84,10 @@ describe('Archer class methods tests', () => { }); it('Health should decrease by the number of damage units', () => { - newArcher.takeDamage(45, opponent, opponent.currentSkill); + newArcher.takeDamage(45, opponent.currentSkill); expect(newArcher.health).toBe(75 - 45); }); - it('Strength should icnrease', () => { - newArcher.damageUp(2); - expect(newArcher.strength).toBe(27); - }); - it('Should change the propertie "skillUsed" to true', () => { newArcher.choseSkill(); newArcher.useSkill(opponent); @@ -110,13 +105,13 @@ describe('Archer class methods tests', () => { }); it('Ibragim should DIE.', () => { - newArcher.takeDamage(newArcher.initialHealth, opponent, opponent.currentSkill); + newArcher.takeDamage(newArcher.initialHealth, opponent.currentSkill); expect(newArcher.isAlive).toBe(false); expect(newArcher.health).toBe(0); }); it('Ibragim health should be equal 0.', () => { - newArcher.takeDamage(1000, opponent, opponent.currentSkill); + newArcher.takeDamage(1000, opponent.currentSkill); expect(newArcher.health).toBe(0); }); @@ -138,7 +133,7 @@ describe('Archer class methods tests', () => { newArcher.useSkill(opponent, 'огненные стрелы'); newArcher.attack(opponent); newArcher.attack(opponent); - expect(newArcher.strength).toBe(27); + expect(newArcher.strength).toBe(25); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Game.spec.ts b/rpgsaga/saga/tests/sagaTests/Game.spec.ts index 90da669c1..9fd8eaee4 100644 --- a/rpgsaga/saga/tests/sagaTests/Game.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Game.spec.ts @@ -64,7 +64,7 @@ describe('Game tests', () => { it('Should handle a battle where one player is already dead', async () => { const player1 = playerFabric.createRandomPlayer(); const player2 = playerFabric.createRandomPlayer(); - player2.takeDamage(player2.health, player1); + player2.takeDamage(player2.health); const winner = await game.battle([player1, player2]); expect(winner).toBe(player1); }); diff --git a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts index 4bd45400b..fbf08bd89 100644 --- a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -84,7 +84,7 @@ describe('Knight class methods tests', () => { }); it('Health should decrease by the number of damage units', () => { - newKnight.takeDamage(45, opponent, opponent.currentSkill); + newKnight.takeDamage(45, opponent.currentSkill); expect(newKnight.health).toBe(75 - 45); }); @@ -97,11 +97,6 @@ describe('Knight class methods tests', () => { ); }); - it('Strength should icnrease', () => { - newKnight.damageUp(2); - expect(newKnight.strength).toBe(27); - }); - it('Should change the propertie "skillUsed" to true', () => { newKnight.choseSkill(); newKnight.useSkill(opponent); @@ -121,13 +116,13 @@ describe('Knight class methods tests', () => { }); it('Ibragim should DIE.', () => { - newKnight.takeDamage(newKnight.initialHealth, opponent); + newKnight.takeDamage(newKnight.initialHealth); expect(newKnight.isAlive).toBe(false); expect(newKnight.health).toBe(0); }); it('Ibragim health should be equal 0.', () => { - newKnight.takeDamage(1000, opponent, opponent.currentSkill); + newKnight.takeDamage(1000, opponent.currentSkill); expect(newKnight.health).toBe(0); }); diff --git a/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts index 8c36b2fc5..712e7055b 100644 --- a/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/SkillFabric.spec.ts @@ -12,15 +12,18 @@ describe('SkillFabric tests', () => { expect(skill?.name).toBe('огненные стрелы'); expect(skill?.usageCount).toBe(1); expect(skill?.initialSkillUsage).toBe(1); - expect(skill?.effect).toBeDefined(); + expect(skill?.effect).toBeUndefined(); }); it('Should create a skill from template with turns', () => { const skill = skillFabric.createSkillFromTemplate('ледяные стрелы'); expect(skill).toBeDefined(); expect(skill?.name).toBe('ледяные стрелы'); + expect(skill?.usageCount).toBe(1); + expect(skill?.initialSkillUsage).toBe(1); expect(skill?.turns).toBe(3); expect(skill?.initialTurns).toBe(3); + expect(skill?.effect).toBeUndefined(); }); it('Should create a skill from template with damage calculation', () => { @@ -58,12 +61,10 @@ describe('SkillFabric tests', () => { const opponent = new Archer(100, 10, '', weaponFabric.createRandomWeapon('bow'), []); const fireArrows = skillFabric.createSkillFromTemplate('огненные стрелы')!; - fireArrows.effect!(player, opponent); - expect(player.strength).toBe(22); + expect(player.strength + fireArrows.buff.strength).toBe(22); const iceArrows = skillFabric.createSkillFromTemplate('ледяные стрелы')!; - iceArrows.effect!(player, opponent); - expect(player.strength).toBe(25); + expect(player.strength + iceArrows.buff.strength).toBe(23); const vengeanceStrike = skillFabric.createSkillFromTemplate('удар возмездия')!; vengeanceStrike.effect!(player, opponent); @@ -71,6 +72,6 @@ describe('SkillFabric tests', () => { const enchantment = skillFabric.createSkillFromTemplate('заворожение')!; enchantment.effect!(player, opponent); - expect(opponent.health).toBeLessThan(100); + expect(opponent.countOfSkipingTurns).toBe(1); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts index 54155eaf4..f2dab58d2 100644 --- a/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts @@ -83,15 +83,10 @@ describe('Wizard class methods tests', () => { }); it('Health should decrease by the number of damage units', () => { - newWizard.takeDamage(45, opponent, opponent.currentSkill); + newWizard.takeDamage(45, opponent.currentSkill); expect(newWizard.health).toBe(75 - 45); }); - it('Strength should icnrease', () => { - newWizard.damageUp(2); - expect(newWizard.strength).toBe(27); - }); - it('Should change the propertie "skillUsed" to true', () => { newWizard.choseSkill(); newWizard.useSkill(opponent); @@ -109,13 +104,13 @@ describe('Wizard class methods tests', () => { }); it('Ibragim should DIE.', () => { - newWizard.takeDamage(newWizard.initialHealth, opponent, opponent.currentSkill); + newWizard.takeDamage(newWizard.initialHealth, opponent.currentSkill); expect(newWizard.isAlive).toBe(false); expect(newWizard.health).toBe(0); }); it('Ibragim health should be equal 0.', () => { - newWizard.takeDamage(1000, opponent, opponent.currentSkill); + newWizard.takeDamage(1000, opponent.currentSkill); expect(newWizard.health).toBe(0); }); From f2b1ffea948fcedb1eeeb53de509169da8f5c83b Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 18:13:35 +0300 Subject: [PATCH 21/27] attack and take damage fixes --- rpgsaga/saga/src/game/abstract/Player.ts | 9 +++++---- rpgsaga/saga/src/game/classes/Knight.ts | 5 ++++- rpgsaga/saga/src/game/gameplay/Game.ts | 3 +-- rpgsaga/saga/src/game/utils/output/Logger.ts | 6 ++---- rpgsaga/saga/tests/sagaTests/Knight.spec.ts | 8 ++------ 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index 18af2f233..a104dc10b 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -113,7 +113,7 @@ export abstract class Player { } } - public attack(opponent: Player): void { + public attack(opponent: Player): number { if (this.countOfSkipingTurns > 0) { this._countOfSkipingTurns--; return; @@ -127,9 +127,9 @@ export abstract class Player { this._updateSkills(); } - opponent.takeDamage(this._strength + this._weapon.damage, this._currentSkill); + return opponent.takeDamage(this._strength + this._weapon.damage, this._currentSkill); } else { - opponent.takeDamage(this._strength + this._weapon.damage); + return opponent.takeDamage(this._strength + this._weapon.damage); } } @@ -148,7 +148,7 @@ export abstract class Player { } } - public takeDamage(damage: number, skill: ISkill | undefined = undefined): void { + public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; if (skill !== undefined && skill.buff) { currentDamage += skill.buff.strength; @@ -158,6 +158,7 @@ export abstract class Player { this._health = 0; this._isAlive = false; } + return currentDamage; } public heal(amount: number) { diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts index fed1d5820..37c3dea67 100644 --- a/rpgsaga/saga/src/game/classes/Knight.ts +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -15,15 +15,18 @@ export class Knight extends Player { super(playerHealth, playerStrength, playerName, playerWeapon, playerSkills); } - public takeDamage(damage: number, skill: ISkill | undefined = undefined): void { + public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; if (skill !== undefined && skill.name === 'ледяные стрелы') { currentDamage = damage; + } else if (skill !== undefined && skill.buff) { + currentDamage = damage += skill.buff.strength; } this._health -= currentDamage; if (this._health <= 0) { this._health = 0; this._isAlive = false; } + return currentDamage; } } diff --git a/rpgsaga/saga/src/game/gameplay/Game.ts b/rpgsaga/saga/src/game/gameplay/Game.ts index dc969d9c7..624316c0e 100644 --- a/rpgsaga/saga/src/game/gameplay/Game.ts +++ b/rpgsaga/saga/src/game/gameplay/Game.ts @@ -59,8 +59,7 @@ export class Game { if (defender.isAlive) { if (attacker.countOfSkipingTurns === 0) { - attacker.attack(defender); - this.logger.attackLog(attacker, defender); + this.logger.attackLog(attacker, defender, attacker.attack(defender)); } else { attacker.attack(defender); this.logger.skipTurnLog(attacker, defender); diff --git a/rpgsaga/saga/src/game/utils/output/Logger.ts b/rpgsaga/saga/src/game/utils/output/Logger.ts index 8ac29d392..ecbecf10d 100644 --- a/rpgsaga/saga/src/game/utils/output/Logger.ts +++ b/rpgsaga/saga/src/game/utils/output/Logger.ts @@ -18,11 +18,9 @@ export class Logger { console.log(logEntry); } - public attackLog(attacker: Player, defender: Player): void { + public attackLog(attacker: Player, defender: Player, damage: number): void { const timestamp: string = new Date().toISOString(); - const message: string = `(${attacker.className}) ${attacker.name} наносит урон ${ - attacker.strength + attacker.weapon!.damage! - } на ${defender.name} (${defender.className})`; + const message: string = `(${attacker.className}) ${attacker.name} наносит урон ${damage} на ${defender.name} (${defender.className})`; const logEntry: string = `${timestamp}: ${message}\n`; console.log(logEntry); } diff --git a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts index fbf08bd89..eb845f23f 100644 --- a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -92,9 +92,7 @@ describe('Knight class methods tests', () => { newKnight.heal(100); opponent.useSkill(newKnight, 'ледяные стрелы'); opponent.attack(newKnight); - expect(newKnight.health).toBe( - 75 - (opponent.strength - opponent.currentSkill!.buff!.strength + opponent.weapon.damage), - ); + expect(newKnight.health).toBe(75 - (opponent.strength + opponent.weapon.damage)); }); it('Should change the propertie "skillUsed" to true', () => { @@ -105,9 +103,7 @@ describe('Knight class methods tests', () => { it('Health should icnrease', () => { newKnight.heal(10); - expect(newKnight.health).toBe( - 75 - (opponent.strength - opponent.currentSkill!.buff!.strength + opponent.weapon.damage) + 10, - ); + expect(newKnight.health).toBe(75 - (opponent.strength + opponent.weapon.damage) + 10); }); it('Health should be equal initialHealth', () => { From 12dc69bf2c982c139c887a75c0c352b7e5435e38 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 18:15:06 +0300 Subject: [PATCH 22/27] minor fix --- rpgsaga/saga/src/game/classes/Knight.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts index 37c3dea67..9168ff361 100644 --- a/rpgsaga/saga/src/game/classes/Knight.ts +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -17,9 +17,7 @@ export class Knight extends Player { public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; - if (skill !== undefined && skill.name === 'ледяные стрелы') { - currentDamage = damage; - } else if (skill !== undefined && skill.buff) { + if (skill !== undefined && skill.name !== 'ледяные стрелы' && skill.buff) { currentDamage = damage += skill.buff.strength; } this._health -= currentDamage; From 020e22274183a9635e02fb4c83855de63c9fb4ce Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sun, 5 Jan 2025 18:16:18 +0300 Subject: [PATCH 23/27] minor fix --- rpgsaga/saga/src/game/classes/Knight.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts index 9168ff361..ab1586c4f 100644 --- a/rpgsaga/saga/src/game/classes/Knight.ts +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -18,7 +18,7 @@ export class Knight extends Player { public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; if (skill !== undefined && skill.name !== 'ледяные стрелы' && skill.buff) { - currentDamage = damage += skill.buff.strength; + currentDamage = damage + skill.buff.strength; } this._health -= currentDamage; if (this._health <= 0) { From 30db7eeefeb00939457e9ab4c46973b9afec761e Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 10 Jan 2025 23:42:35 +0300 Subject: [PATCH 24/27] damage changes --- rpgsaga/saga/src/game/abstract/Player.ts | 72 +++++++++---------- rpgsaga/saga/src/game/classes/Knight.ts | 4 +- .../fabrics/weaponsFabric/WeaponFabric.ts | 47 +++--------- rpgsaga/saga/src/game/gameplay/Game.ts | 8 +-- .../saga/src/game/utils/input/createGame.ts | 2 +- rpgsaga/saga/src/game/utils/output/Logger.ts | 17 +---- rpgsaga/saga/src/game/weapon/IWeapon.ts | 1 - rpgsaga/saga/tests/sagaTests/Archer.spec.ts | 18 ++--- rpgsaga/saga/tests/sagaTests/Knight.spec.ts | 16 ++--- .../saga/tests/sagaTests/WeaponFabric.spec.ts | 25 ++----- rpgsaga/saga/tests/sagaTests/Wizard.spec.ts | 15 ++-- 11 files changed, 72 insertions(+), 153 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index a104dc10b..b6f6aff9d 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -11,8 +11,6 @@ export abstract class Player { protected _strength: number; protected _skills: ISkill[]; protected _currentSkill?: ISkill; - protected skillBuff: number = 0; - protected _isSkillUsed: boolean = false; protected _isAlive: boolean = true; protected _countOfSkipingTurns: number = 0; protected _weapon: IWeapon; @@ -25,9 +23,9 @@ export abstract class Player { playerSkills: ISkill[], ) { this._initialHealth = playerHealth; - this._health = this.initialHealth; + this._health = this._initialHealth; this._initialStrength = playerStrength; - this._strength = this.initialStrength; + this._strength = this._initialStrength; this._name = playerName; this._weapon = playerWeapon; this._skills = playerSkills; @@ -45,10 +43,6 @@ export abstract class Player { return this._isAlive; } - public get isSkillUsed(): boolean { - return this._isSkillUsed; - } - public get health(): number { return this._health; } @@ -85,18 +79,13 @@ export abstract class Player { this._currentSkill = getRandomArrayElement(this.skills); } - public useSkill(opponent: Player, skillName: string | null = null): void { + public useSkill(opponent: Player, skillName: string | null = null): boolean { if (this.skills.length === 0) { return; } if (skillName !== null) { - this.skills.forEach(skill => { - if (skill.name === skillName.toLowerCase()) { - this._currentSkill = skill; - return; - } - }); + this._currentSkill = this.skills.find(skill => skill.name === skillName.toLowerCase()); } if (this._currentSkill !== undefined && this._currentSkill.usageCount > 0) { @@ -105,54 +94,57 @@ export abstract class Player { } this._currentSkill.usageCount--; this.skills.forEach(skill => { - if (skill.name === this._currentSkill!.name) { + if (skill.name === this._currentSkill.name) { skill.usageCount--; } }); - this._isSkillUsed = true; + + return true; } + + return false; } public attack(opponent: Player): number { if (this.countOfSkipingTurns > 0) { this._countOfSkipingTurns--; - return; + return 0; } if (this._currentSkill) { - const skillIndex = this._skills.findIndex(skill => skill.name === this._currentSkill!.name); + const skillIndex = this._skills.findIndex(skill => skill.name === this._currentSkill.name); + + let skillsBuff: number = 0; if (skillIndex !== -1) { this._skills[skillIndex].isUsed = true; - this._updateSkills(); + this._skills.forEach(skill => { + if (skill.isUsed && skill.buff) { + if (skill.turns <= 0) { + skill.turns = skill.initialTurns; + skill.isUsed = false; + } + if (skill.turns > 0) { + skill.turns--; + this._currentSkill.turns--; + } + skillsBuff += skill.buff.strength; + } + }); + + if (this._currentSkill.turns <= 0) { + this._currentSkill = undefined; + } } - return opponent.takeDamage(this._strength + this._weapon.damage, this._currentSkill); + return opponent.takeDamage(this._strength + this._weapon.damage + skillsBuff, this._currentSkill); } else { return opponent.takeDamage(this._strength + this._weapon.damage); } } - protected _updateSkills(): void { - for (const skill of this._skills) { - if (skill.isUsed) { - if (skill.turns! <= 0) { - if (skill.buff) { - this._strength -= skill.buff.strength; - } - skill.isUsed = false; - skill.turns = skill.initialTurns; - } - skill.turns--; - } - } - } - public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; - if (skill !== undefined && skill.buff) { - currentDamage += skill.buff.strength; - } this._health -= currentDamage; if (this._health <= 0) { this._health = 0; @@ -172,7 +164,7 @@ export abstract class Player { public reset(): void { this._health = this.initialHealth; this._strength = this.initialStrength; - this._isSkillUsed = false; + this._currentSkill = undefined; this._skills.forEach(skill => { skill.usageCount = skill.initialSkillUsage; skill.isUsed = false; diff --git a/rpgsaga/saga/src/game/classes/Knight.ts b/rpgsaga/saga/src/game/classes/Knight.ts index ab1586c4f..e9383ee06 100644 --- a/rpgsaga/saga/src/game/classes/Knight.ts +++ b/rpgsaga/saga/src/game/classes/Knight.ts @@ -17,8 +17,8 @@ export class Knight extends Player { public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { let currentDamage: number = damage; - if (skill !== undefined && skill.name !== 'ледяные стрелы' && skill.buff) { - currentDamage = damage + skill.buff.strength; + if (skill !== undefined && skill.name === 'ледяные стрелы' && skill.buff) { + currentDamage -= skill.buff.strength; } this._health -= currentDamage; if (this._health <= 0) { diff --git a/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts index 1f0cb5544..41621c6e5 100644 --- a/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts +++ b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts @@ -1,78 +1,53 @@ import { getRandomArrayElement, getRandomNumber } from '../../utils/randomization'; import { IWeapon } from '../../weapon/IWeapon'; -export class WeaponFabric implements IWeapon { +export class WeaponFabric { private names: object = { sword: ['Dragonsbane', 'Stormbringer', 'Aethelred'], stick: ['Oak Staff', 'Elderwood Branch', "Shepherd's Crook"], bow: ["Hunter's Bow", 'Longbow', 'Shortbow'], }; - private types: string[] = ['огонь', 'яд', 'лёд']; - private _name: string = ''; - private _typeOfDamage: string = ''; - private _damage: number = 0; - - public get name(): string { - return this._name; - } - - public get typeOfDamage(): string { - return this._typeOfDamage; - } - - public get damage(): number { - return this._damage; - } - - public createWeapon(weaponType: string, weaponDamageType: string, weaponName: string, weaponDamage: number) { + public createWeapon(weaponType: string, weaponName: string, weaponDamage: number) { let weapon: IWeapon; - this._name = weaponName; - this._damage = weaponDamage; - this._typeOfDamage = weaponDamageType; switch (weaponType.toLowerCase()) { case 'sword': weapon = { - name: this.name, - damage: this.damage, - typeOfDamage: this.typeOfDamage, + name: weaponName, + damage: weaponDamage, }; break; case 'stick': weapon = { - name: this.name, - damage: this.damage, - typeOfDamage: this.typeOfDamage, + name: weaponName, + damage: weaponDamage, }; break; case 'bow': weapon = { - name: this.name, - damage: this.damage, - typeOfDamage: this.typeOfDamage, + name: weaponName, + damage: weaponDamage, }; break; default: weapon = { name: 'fists', damage: 3, - typeOfDamage: '-', }; } return weapon; } public createRandomWeapon(weaponType: string): IWeapon { - const damageType = getRandomArrayElement(this.types)!; const namesArr: string[] = this.names[weaponType.toLowerCase() as keyof typeof this.names]; if (!namesArr) { - return this.createWeapon('fists', '-', 'fists', 3); + return this.createWeapon('fists', 'fists', 3); } const name = this.names[weaponType.toLowerCase() as keyof typeof this.names][Math.floor(Math.random() * namesArr.length)]; - const damage = getRandomNumber(5, 10); - return this.createWeapon(weaponType, damageType, name, damage); + const damage = getRandomNumber(2, 5); + return this.createWeapon(weaponType, name, damage); } } diff --git a/rpgsaga/saga/src/game/gameplay/Game.ts b/rpgsaga/saga/src/game/gameplay/Game.ts index 624316c0e..d89b0f1a7 100644 --- a/rpgsaga/saga/src/game/gameplay/Game.ts +++ b/rpgsaga/saga/src/game/gameplay/Game.ts @@ -38,9 +38,8 @@ export class Game { const player1 = players[i]; const player2 = players[i + 1]; const winner = await this.battle([player1, player2]); + winner.reset(); nextRoundPlayers.push(winner); - player1.reset(); - player2.reset(); } return this.tournament(nextRoundPlayers); @@ -73,8 +72,8 @@ export class Game { if (Math.random() < 0.4 && attacker.isAlive && defender.isAlive) { attacker.choseSkill(); - if (attacker.currentSkill!.usageCount! > 0) { - attacker.useSkill(defender); + const isUsed: boolean = attacker.useSkill(defender); + if (isUsed) { this.logger.skillLog(attacker, defender); } } @@ -90,6 +89,7 @@ export class Game { private delay(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } + private updatePlayersArray() { this._players = this._players.filter(player => player.isAlive); } diff --git a/rpgsaga/saga/src/game/utils/input/createGame.ts b/rpgsaga/saga/src/game/utils/input/createGame.ts index 6207cc112..27937a6f3 100644 --- a/rpgsaga/saga/src/game/utils/input/createGame.ts +++ b/rpgsaga/saga/src/game/utils/input/createGame.ts @@ -24,7 +24,7 @@ export function createGame(): void { const game = new Game(number, undefined, logger); switch (inputString.toLowerCase()) { case 'да': - createCharacter(number); + await createCharacter(number); break; case 'нет': await game.start(); diff --git a/rpgsaga/saga/src/game/utils/output/Logger.ts b/rpgsaga/saga/src/game/utils/output/Logger.ts index ecbecf10d..e1df7c3a7 100644 --- a/rpgsaga/saga/src/game/utils/output/Logger.ts +++ b/rpgsaga/saga/src/game/utils/output/Logger.ts @@ -1,17 +1,6 @@ import { Player } from '../../abstract/Player'; export class Logger { - // static _instance: Logger; - - constructor() {} - - // public get instance(): Logger { - // if (!Logger._instance) { - // Logger._instance = new Logger(); - // } - // return Logger._instance; - // } - public messageLog(message: string): void { const timestamp: string = new Date().toISOString(); const logEntry: string = `${timestamp}: ${message}\n`; @@ -20,7 +9,7 @@ export class Logger { public attackLog(attacker: Player, defender: Player, damage: number): void { const timestamp: string = new Date().toISOString(); - const message: string = `(${attacker.className}) ${attacker.name} наносит урон ${damage} на ${defender.name} (${defender.className})`; + const message: string = `(${attacker.className}) ${attacker.name} наносит урон ${damage} игроку ${defender.name} (${defender.className})`; const logEntry: string = `${timestamp}: ${message}\n`; console.log(logEntry); } @@ -38,9 +27,7 @@ export class Logger { public skipTurnLog(attacker: Player, defender: Player): void { const timestamp: string = new Date().toISOString(); - const message: string = `(${attacker.className}) ${attacker.name} пропускает ход из-за ${ - defender.currentSkill!.name - }`; + const message: string = `(${attacker.className}) ${attacker.name} пропускает ход из-за ${defender.currentSkill.name}`; const logEntry: string = `${timestamp}: ${message}\n`; console.log(logEntry); } diff --git a/rpgsaga/saga/src/game/weapon/IWeapon.ts b/rpgsaga/saga/src/game/weapon/IWeapon.ts index 22e513405..92984e90e 100644 --- a/rpgsaga/saga/src/game/weapon/IWeapon.ts +++ b/rpgsaga/saga/src/game/weapon/IWeapon.ts @@ -1,5 +1,4 @@ export interface IWeapon { get name(): string; get damage(): number; - get typeOfDamage(): string; } diff --git a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts index 195197144..6b762e72e 100644 --- a/rpgsaga/saga/tests/sagaTests/Archer.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Archer.spec.ts @@ -39,14 +39,6 @@ describe('Archer class methods tests', () => { it('IsAlive get test', () => { expect(newArcher.isAlive).toBe(true); }); - it('IsSkillUsed get test', () => { - expect(newArcher.isSkillUsed).toBe(false); - }); - it('IsSkillUsed get test after using skill', () => { - newArcher.choseSkill(); - newArcher.useSkill(newArcher); - expect(newArcher.isSkillUsed).toBe(true); - }); it('InitialHealth get test', () => { expect(newArcher.initialHealth).toBe(75); }); @@ -91,7 +83,7 @@ describe('Archer class methods tests', () => { it('Should change the propertie "skillUsed" to true', () => { newArcher.choseSkill(); newArcher.useSkill(opponent); - expect(newArcher.isSkillUsed).toBe(true); + expect(newArcher.skills).toContain(newArcher.currentSkill); }); it('Health should icnrease', () => { @@ -104,7 +96,7 @@ describe('Archer class methods tests', () => { expect(newArcher.health).toBe(newArcher.initialHealth); }); - it('Ibragim should DIE.', () => { + it('Ibragim should die.', () => { newArcher.takeDamage(newArcher.initialHealth, opponent.currentSkill); expect(newArcher.isAlive).toBe(false); expect(newArcher.health).toBe(0); @@ -119,7 +111,7 @@ describe('Archer class methods tests', () => { newArcher.reset(); expect(newArcher.health).toBe(newArcher.initialHealth); expect(newArcher.strength).toBe(newArcher.initialStrength); - expect(newArcher.isSkillUsed).toBe(false); + expect(newArcher.currentSkill).toBeUndefined(); newArcher.skills!.forEach(skill => { expect(skill.usageCount).toBe(skill.initialSkillUsage); expect(skill.isUsed).toBe(false); @@ -131,9 +123,9 @@ describe('Archer class methods tests', () => { newArcher.useSkill(opponent, 'ледяные стрелы'); newArcher.attack(opponent); newArcher.useSkill(opponent, 'огненные стрелы'); + expect(newArcher.attack(opponent)).toBe(newArcher.strength + newArcher.weapon.damage + 5); newArcher.attack(opponent); - newArcher.attack(opponent); - expect(newArcher.strength).toBe(25); + expect(newArcher.attack(opponent)).toBe(newArcher.strength + newArcher.weapon.damage + 2); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts index eb845f23f..c5e1e9a90 100644 --- a/rpgsaga/saga/tests/sagaTests/Knight.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Knight.spec.ts @@ -39,14 +39,6 @@ describe('Knight class methods tests', () => { it('IsAlive get test', () => { expect(newKnight.isAlive).toBe(true); }); - it('IsSkillUsed get test', () => { - expect(newKnight.isSkillUsed).toBe(false); - }); - it('IsSkillUsed get test after using skill', () => { - newKnight.choseSkill(); - newKnight.useSkill(newKnight); - expect(newKnight.isSkillUsed).toBe(true); - }); it('InitialHealth get test', () => { expect(newKnight.initialHealth).toBe(75); }); @@ -98,7 +90,7 @@ describe('Knight class methods tests', () => { it('Should change the propertie "skillUsed" to true', () => { newKnight.choseSkill(); newKnight.useSkill(opponent); - expect(newKnight.isSkillUsed).toBe(true); + expect(newKnight.skills).toContain(newKnight.currentSkill); }); it('Health should icnrease', () => { @@ -111,7 +103,7 @@ describe('Knight class methods tests', () => { expect(newKnight.health).toBe(newKnight.initialHealth); }); - it('Ibragim should DIE.', () => { + it('Ibragim should die.', () => { newKnight.takeDamage(newKnight.initialHealth); expect(newKnight.isAlive).toBe(false); expect(newKnight.health).toBe(0); @@ -126,7 +118,7 @@ describe('Knight class methods tests', () => { newKnight.reset(); expect(newKnight.health).toBe(newKnight.initialHealth); expect(newKnight.strength).toBe(newKnight.initialStrength); - expect(newKnight.isSkillUsed).toBe(false); + expect(newKnight.currentSkill).toBeUndefined(); newKnight.skills!.forEach(skill => { expect(skill.usageCount).toBe(skill.initialSkillUsage); expect(skill.isUsed).toBe(false); @@ -139,7 +131,7 @@ describe('Knight class methods tests', () => { newKnight.attack(opponent); newKnight.attack(opponent); newKnight.attack(opponent); - expect(newKnight.strength).toBe(25); + expect(newKnight.attack(opponent)).toBe(newKnight.strength + newKnight.weapon.damage); }); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts b/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts index a2a6f040d..ec754c6cd 100644 --- a/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/WeaponFabric.spec.ts @@ -4,67 +4,56 @@ describe('WeaponFabric', () => { const weaponFabric = new WeaponFabric(); it('should create a sword', () => { - const weapon = weaponFabric.createWeapon('sword', 'огонь', 'Dragonsbane', 10); + const weapon = weaponFabric.createWeapon('sword', 'Dragonsbane', 10); expect(weapon.name).toBe('Dragonsbane'); - expect(weapon.typeOfDamage).toBe('огонь'); expect(weapon.damage).toBe(10); }); it('should create a stick', () => { - const weapon = weaponFabric.createWeapon('stick', 'яд', 'Oak Staff', 5); + const weapon = weaponFabric.createWeapon('stick', 'Oak Staff', 5); expect(weapon.name).toBe('Oak Staff'); - expect(weapon.typeOfDamage).toBe('яд'); expect(weapon.damage).toBe(5); }); it('should create a bow', () => { - const weapon = weaponFabric.createWeapon('bow', 'лёд', 'Longbow', 8); + const weapon = weaponFabric.createWeapon('bow', 'Longbow', 8); expect(weapon.name).toBe('Longbow'); - expect(weapon.typeOfDamage).toBe('лёд'); expect(weapon.damage).toBe(8); }); it('should create fists as default', () => { - const weapon = weaponFabric.createWeapon('invalidType', 'invalidDamageType', 'invalidName', 0); + const weapon = weaponFabric.createWeapon('invalidType', 'invalidName', 0); expect(weapon.name).toBe('fists'); - expect(weapon.typeOfDamage).toBe('-'); expect(weapon.damage).toBe(3); }); it('should create a random sword', () => { const weapon = weaponFabric.createRandomWeapon('sword'); expect(weapon.name).toBeDefined(); - expect(weapon.typeOfDamage).toBeDefined(); - expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeGreaterThanOrEqual(2); expect(weapon.damage).toBeLessThanOrEqual(10); expect(['Dragonsbane', 'Stormbringer', 'Aethelred']).toContain(weapon.name); - expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); }); it('should create a random stick', () => { const weapon = weaponFabric.createRandomWeapon('stick'); expect(weapon.name).toBeDefined(); - expect(weapon.typeOfDamage).toBeDefined(); - expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeGreaterThanOrEqual(2); expect(weapon.damage).toBeLessThanOrEqual(10); expect(['Oak Staff', 'Elderwood Branch', "Shepherd's Crook"]).toContain(weapon.name); - expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); }); it('should create a random bow', () => { const weapon = weaponFabric.createRandomWeapon('bow'); expect(weapon.name).toBeDefined(); - expect(weapon.typeOfDamage).toBeDefined(); - expect(weapon.damage).toBeGreaterThanOrEqual(5); + expect(weapon.damage).toBeGreaterThanOrEqual(2); expect(weapon.damage).toBeLessThanOrEqual(10); expect(["Hunter's Bow", 'Longbow', 'Shortbow']).toContain(weapon.name); - expect(['огонь', 'яд', 'лёд']).toContain(weapon.typeOfDamage); }); it('should handle invalid weapon type in createRandomWeapon', () => { const weapon = weaponFabric.createRandomWeapon('invalidType'); expect(weapon.name).toBe('fists'); - expect(weapon.typeOfDamage).toBe('-'); expect(weapon.damage).toBe(3); }); }); diff --git a/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts index f2dab58d2..6eb70dc93 100644 --- a/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts +++ b/rpgsaga/saga/tests/sagaTests/Wizard.spec.ts @@ -39,13 +39,6 @@ describe('Wizard class methods tests', () => { it('IsAlive get test', () => { expect(newWizard.isAlive).toBe(true); }); - it('IsSkillUsed get test', () => { - expect(newWizard.isSkillUsed).toBe(false); - }); - it('IsSkillUsed get test after using skill', () => { - newWizard.useSkill(newWizard, 'ледяные стрелы'); - expect(newWizard.isSkillUsed).toBe(true); - }); it('InitialHealth get test', () => { expect(newWizard.initialHealth).toBe(75); }); @@ -90,7 +83,7 @@ describe('Wizard class methods tests', () => { it('Should change the propertie "skillUsed" to true', () => { newWizard.choseSkill(); newWizard.useSkill(opponent); - expect(newWizard.isSkillUsed).toBe(true); + expect(newWizard.skills).toContain(newWizard.currentSkill); }); it('Health should icnrease', () => { @@ -103,7 +96,7 @@ describe('Wizard class methods tests', () => { expect(newWizard.health).toBe(newWizard.initialHealth); }); - it('Ibragim should DIE.', () => { + it('Ibragim should die.', () => { newWizard.takeDamage(newWizard.initialHealth, opponent.currentSkill); expect(newWizard.isAlive).toBe(false); expect(newWizard.health).toBe(0); @@ -118,7 +111,7 @@ describe('Wizard class methods tests', () => { newWizard.reset(); expect(newWizard.health).toBe(newWizard.initialHealth); expect(newWizard.strength).toBe(newWizard.initialStrength); - expect(newWizard.isSkillUsed).toBe(false); + expect(newWizard.currentSkill).toBeUndefined(); newWizard.skills!.forEach(skill => { expect(skill.usageCount).toBe(skill.initialSkillUsage); expect(skill.isUsed).toBe(false); @@ -131,7 +124,7 @@ describe('Wizard class methods tests', () => { newWizard.attack(opponent); newWizard.attack(opponent); newWizard.attack(opponent); - expect(newWizard.strength).toBe(25); + expect(newWizard.attack(opponent)).toBe(newWizard.strength + newWizard.weapon.damage); }); }); }); From 241d907404ce2c106775135b50f11e7ab4700b12 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 10 Jan 2025 23:44:49 +0300 Subject: [PATCH 25/27] lint fix --- rpgsaga/saga/src/game/abstract/Player.ts | 12 +++++++----- .../src/game/fabrics/weaponsFabric/WeaponFabric.ts | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index b6f6aff9d..03413663e 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -144,11 +144,13 @@ export abstract class Player { } public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { - let currentDamage: number = damage; - this._health -= currentDamage; - if (this._health <= 0) { - this._health = 0; - this._isAlive = false; + const currentDamage: number = damage; + if (skill === undefined) { + this._health -= currentDamage; + if (this._health <= 0) { + this._health = 0; + this._isAlive = false; + } } return currentDamage; } diff --git a/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts index 41621c6e5..d2884f54d 100644 --- a/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts +++ b/rpgsaga/saga/src/game/fabrics/weaponsFabric/WeaponFabric.ts @@ -1,4 +1,4 @@ -import { getRandomArrayElement, getRandomNumber } from '../../utils/randomization'; +import { getRandomNumber } from '../../utils/randomization'; import { IWeapon } from '../../weapon/IWeapon'; export class WeaponFabric { From c024254a92ea3f0bb533aa575a0c9995e151746e Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Fri, 10 Jan 2025 23:45:57 +0300 Subject: [PATCH 26/27] lint fix --- rpgsaga/saga/src/game/abstract/Player.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/rpgsaga/saga/src/game/abstract/Player.ts b/rpgsaga/saga/src/game/abstract/Player.ts index 03413663e..cdc83776b 100644 --- a/rpgsaga/saga/src/game/abstract/Player.ts +++ b/rpgsaga/saga/src/game/abstract/Player.ts @@ -145,12 +145,10 @@ export abstract class Player { public takeDamage(damage: number, skill: ISkill | undefined = undefined): number { const currentDamage: number = damage; - if (skill === undefined) { - this._health -= currentDamage; - if (this._health <= 0) { - this._health = 0; - this._isAlive = false; - } + this._health -= currentDamage; + if (this._health <= 0) { + this._health = 0; + this._isAlive = false; } return currentDamage; } From a99bd60176bdf93fca05bbc5ffa0acda9c32a5d0 Mon Sep 17 00:00:00 2001 From: J0es1ick Date: Sat, 11 Jan 2025 00:11:36 +0300 Subject: [PATCH 27/27] randomization tests --- .../sagaTests/getRandomArrayElement.spec.ts | 38 ++++++++++++ .../tests/sagaTests/getRandomNumber.spec.ts | 61 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 rpgsaga/saga/tests/sagaTests/getRandomArrayElement.spec.ts create mode 100644 rpgsaga/saga/tests/sagaTests/getRandomNumber.spec.ts diff --git a/rpgsaga/saga/tests/sagaTests/getRandomArrayElement.spec.ts b/rpgsaga/saga/tests/sagaTests/getRandomArrayElement.spec.ts new file mode 100644 index 000000000..f4923975a --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/getRandomArrayElement.spec.ts @@ -0,0 +1,38 @@ +import { getRandomArrayElement } from '../../src/game/utils/randomization/getRandomArrayElement'; + +describe('getRandomArrayElement tests', () => { + it('Should return undefined for an empty array', () => { + expect(getRandomArrayElement([])).toBeUndefined(); + }); + + it('Should return a random element from a non-empty array', () => { + const arr = [1, 2, 3, 4, 5]; + const element = getRandomArrayElement(arr); + expect(arr).toContain(element!); + }); + + it('Should return a random element from a non-empty array - multiple tests', () => { + const arr = [1, 2, 3, 4, 5]; + for (let i = 0; i < 100; i++) { + const element = getRandomArrayElement(arr); + expect(arr).toContain(element!); + } + }); + + it('Should handle arrays with only one element', () => { + const arr = [1]; + expect(getRandomArrayElement(arr)).toBe(1); + }); + + it('Should handle arrays with different data types', () => { + const arr = [1, 'hello', true, { name: 'test' }]; + const element = getRandomArrayElement(arr); + expect(arr).toContain(element!); + }); + + it('Should handle large arrays correctly', () => { + const arr = Array.from({ length: 1000 }, (_, i) => i + 1); + const element = getRandomArrayElement(arr); + expect(arr).toContain(element!); + }); +}); diff --git a/rpgsaga/saga/tests/sagaTests/getRandomNumber.spec.ts b/rpgsaga/saga/tests/sagaTests/getRandomNumber.spec.ts new file mode 100644 index 000000000..a278888d6 --- /dev/null +++ b/rpgsaga/saga/tests/sagaTests/getRandomNumber.spec.ts @@ -0,0 +1,61 @@ +import { getRandomNumber } from '../../src/game/utils/randomization/getRandomNumber'; + +describe('getRandomNumber tests', () => { + it('Should return -1 if min is greater than max', () => { + expect(getRandomNumber(10, 5)).toBe(-1); + }); + + it('Should return min if min equals max', () => { + expect(getRandomNumber(5, 5)).toBe(5); + }); + + it('Should return a random number within the range [min, max] (inclusive)', () => { + const min = 1; + const max = 10; + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + }); + + it('Should return a random number within the range [min, max] (inclusive) - multiple tests', () => { + const min = 1; + const max = 10; + for (let i = 0; i < 100; i++) { + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + } + }); + + it('Should handle large ranges correctly', () => { + const min = 1; + const max = 100000; + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + }); + + it('Should handle negative ranges correctly', () => { + const min = -10; + const max = 0; + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + }); + + it('Should handle zero as min correctly', () => { + const min = 0; + const max = 10; + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + }); + + it('Should handle zero as max correctly', () => { + const min = -10; + const max = 0; + const randomNumber = getRandomNumber(min, max); + expect(randomNumber).toBeGreaterThanOrEqual(min); + expect(randomNumber).toBeLessThanOrEqual(max); + }); +});