From 63626ce9d0fcc137b131d1f12abde5e951176cc7 Mon Sep 17 00:00:00 2001 From: TiiJay Date: Fri, 29 Nov 2024 20:35:48 +0100 Subject: [PATCH] v.2.0.0 NDameBacktracker implementation --- apps/n-dame-problem/src/n-dame-backtracker.ts | 21 ++++ .../n-dame-problem/src/n-dame-backtracking.ts | 22 ---- apps/n-dame-problem/src/n-dame-model.ts | 106 ++++++++++++------ .../src/n-dame-problem.controller.ts | 15 ++- .../src/n-dame-problem.service.ts | 44 +++++--- .../src/abstract-backtracker.ts | 55 +++++++++ libs/backtracking-lib/src/backtracker.spec.ts | 7 -- libs/backtracking-lib/src/backtracker.ts | 26 ----- ...ace.ts => backtracking-model.interface.ts} | 5 +- package.json | 2 +- 10 files changed, 194 insertions(+), 109 deletions(-) create mode 100644 apps/n-dame-problem/src/n-dame-backtracker.ts delete mode 100644 apps/n-dame-problem/src/n-dame-backtracking.ts create mode 100644 libs/backtracking-lib/src/abstract-backtracker.ts delete mode 100644 libs/backtracking-lib/src/backtracker.spec.ts delete mode 100644 libs/backtracking-lib/src/backtracker.ts rename libs/backtracking-lib/src/{backtracking.interface.ts => backtracking-model.interface.ts} (69%) diff --git a/apps/n-dame-problem/src/n-dame-backtracker.ts b/apps/n-dame-problem/src/n-dame-backtracker.ts new file mode 100644 index 0000000..31e5c84 --- /dev/null +++ b/apps/n-dame-problem/src/n-dame-backtracker.ts @@ -0,0 +1,21 @@ +import { ABacktracker } from '@app/backtracking-lib/abstract-backtracker'; +import { NDameModel } from './n-dame-model'; + +export class NDameBacktracker extends ABacktracker { + private _model: NDameModel; + + constructor(private readonly modelSize: number) { + super(); + this._model = new NDameModel(modelSize); + } + + get model() { + return this._model; + } + + doBacktrack = (): boolean[][] => { + const bSolution = this.findSolution(this._model); + + return this.model.matrix; + }; +} diff --git a/apps/n-dame-problem/src/n-dame-backtracking.ts b/apps/n-dame-problem/src/n-dame-backtracking.ts deleted file mode 100644 index afc8146..0000000 --- a/apps/n-dame-problem/src/n-dame-backtracking.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { NDameModel } from './n-dame-model'; - -export class NDameBacktracking { - private model: NDameModel; - - constructor(private readonly modelSize: number) { - this.model = new NDameModel(modelSize); - } - - options = (): Array => { - // const optionsArray = Array.from( - // { length: this.modelSize }, - // (value, index) => index, - // ); - // wir können nur 1 Dame pro Feld platzieren !!! - return [1]; - }; - - solved = (): boolean => { - return this.model.solved(); - }; -} diff --git a/apps/n-dame-problem/src/n-dame-model.ts b/apps/n-dame-problem/src/n-dame-model.ts index f3d3073..7f7a7a7 100644 --- a/apps/n-dame-problem/src/n-dame-model.ts +++ b/apps/n-dame-problem/src/n-dame-model.ts @@ -1,4 +1,4 @@ -import { IBacktracking } from '@app/backtracking-lib/backtracking.interface'; +import { IBacktrackingModel } from '@app/backtracking-lib/backtracking-model.interface'; import { MatrixPosition } from '@app/backtracking-lib/matrix-position'; import { Logger } from '@nestjs/common'; @@ -7,29 +7,33 @@ enum EDiagonal { NO_SW = -1, } -export class NDameModel implements IBacktracking { +export class NDameModel implements IBacktrackingModel { private readonly logger = new Logger(this.constructor.name); private nDamesMatrix: boolean[][] = []; // Wert initial setzen, damit nextPosition beim ersten Schritt richtig platziert wird private modelPosition: MatrixPosition = new MatrixPosition( -1, - this.modelSize - 1, + this._modelSize - 1, ); - constructor(private readonly modelSize: number) { + constructor(private readonly _modelSize: number) { this.initNDameField(); } private initNDameField() { - for (var zeile = 0; zeile < this.modelSize; zeile++) { + for (var zeile = 0; zeile < this._modelSize; zeile++) { this.nDamesMatrix[zeile] = []; - for (var spalte = 0; spalte < this.modelSize; spalte++) { + for (var spalte = 0; spalte < this._modelSize; spalte++) { this.nDamesMatrix[zeile].push(false); } } } + public get modelSize() { + return this._modelSize; + } + public get matrix(): boolean[][] { return this.nDamesMatrix; } @@ -43,43 +47,58 @@ export class NDameModel implements IBacktracking { // sind alle N Damen auf dem Feld platziert ? public solved = (): boolean => { - return this.dames == this.modelSize; + return this.dames == this._modelSize; }; - options = (): Array => { - // wir können nur 1 Dame pro Feld platzieren !!! - return [1]; + hasNext = (): boolean => { + if ( + // wenn das Maximum erreicht haben ist Schluss + this.modelPosition.row === this.modelSize - 1 && + this.modelPosition.col === this.modelSize - 1 + ) + return false; - // const optionsArray = Array.from( - // { length: this.modelSize }, - // (value, index) => index, - // ); + return true; }; nextPosition = () => { this.modelPosition.col++; - if (this.modelPosition.col == this.modelSize) { + if (this.modelPosition.col == this._modelSize) { this.modelPosition.row++; this.modelPosition.col = 0; } - if (this.modelPosition.row == this.modelSize) + if (this.modelPosition.row == this._modelSize) throw new Error(`model bounds crashed...\n${this.modelPosition}`); return this.modelPosition; }; + nextPossiblePosition = (): boolean => { + try { + var np = this.nextPosition(); + var isPossible = false; + while (!isPossible) { + isPossible = this.positionPossible(); + if (isPossible) return isPossible; + + this.nextPosition(); + } + } catch (error) { + return null; + } + }; + private rowProhibited = (): boolean => { const damesRow = this.nDamesMatrix[this.modelPosition.row]; // steht schon eine Dame in der selben Zeile ? const damesInRow = damesRow.reduce((acc, act) => (act ? ++acc : acc), 0); - if (damesInRow > 1) - this.logger.error(`row occupied: ${this.modelPosition}`); + if (damesInRow > 0) this.logger.warn(`row occupied: ${this.modelPosition}`); - return damesInRow > 1; + return damesInRow > 0; }; private colProhibited = (): boolean => { @@ -91,24 +110,19 @@ export class NDameModel implements IBacktracking { // steht schon eine Dame in der selben Spalte ? const damesInCol = damesCol.reduce((acc, act) => (act ? ++acc : acc), 0); - if (damesInCol > 1) - this.logger.error(`col occupied: ${this.modelPosition}`); + if (damesInCol > 0) this.logger.warn(`col occupied: ${this.modelPosition}`); - return damesInCol > 1; + return damesInCol > 0; }; private damesInDiagonal = (diagDirection: EDiagonal) => { - this.logger.log( - `diaginal ${diagDirection == EDiagonal.NW_SO ? 'NW_SO' : 'NO_SW'}`, - ); - // const startPos = { ...this.modelPosition }; const startPos = new MatrixPosition( this.modelPosition.row, this.modelPosition.col, ); const colSearchEndPos = - diagDirection == EDiagonal.NW_SO ? 0 : this.modelSize - 1; + diagDirection == EDiagonal.NW_SO ? 0 : this._modelSize - 1; var found = false; @@ -124,9 +138,9 @@ export class NDameModel implements IBacktracking { var diagPos = startPos; const diagCoords = []; const damesDiag = []; - const colExitPos = diagDirection == EDiagonal.NW_SO ? this.modelSize : -1; + const colExitPos = diagDirection == EDiagonal.NW_SO ? this._modelSize : -1; while (!diagReady) { - if (diagPos.row == this.modelSize || diagPos.col == colExitPos) + if (diagPos.row == this._modelSize || diagPos.col == colExitPos) diagReady = true; else { diagCoords.push(new MatrixPosition(diagPos.row, diagPos.col)); @@ -136,7 +150,6 @@ export class NDameModel implements IBacktracking { } } - this.logger.verbose(diagCoords.join()); return damesDiag; }; @@ -156,12 +169,12 @@ export class NDameModel implements IBacktracking { 0, ); - if (damesInDiagonalNW_SO > 1) - this.logger.error(`diagNW_SO occupied: ${this.modelPosition}`); - if (damesInDiagonalNO_SW > 1) - this.logger.error(`diagNO_SW occupied: ${this.modelPosition}`); + if (damesInDiagonalNW_SO > 0) + this.logger.warn(`diagNW_SO occupied: ${this.modelPosition}`); + if (damesInDiagonalNO_SW > 0) + this.logger.warn(`diagNO_SW occupied: ${this.modelPosition}`); - return damesInDiagonalNW_SO > 1 || damesInDiagonalNO_SW > 1; + return damesInDiagonalNW_SO > 0 || damesInDiagonalNO_SW > 0; }; // darf hier eine Dame stehen ? @@ -179,14 +192,33 @@ export class NDameModel implements IBacktracking { // Dame auf das Feld platzieren applyPosition = (): MatrixPosition => { - this.logger.warn(`apply: ${JSON.stringify(this.modelPosition)}`); + this.logger.verbose(`apply: ${JSON.stringify(this.modelPosition)}`); this.nDamesMatrix[this.modelPosition.row][this.modelPosition.col] = true; return this.modelPosition; }; // letzte Dame vom Feld nehmen discardPosition = (): MatrixPosition => { - this.nDamesMatrix[this.modelPosition.row][this.modelPosition.col] = false; + // 1. letzte Dame suchen + const lastDameFlatPos = this.nDamesMatrix.flat().lastIndexOf(true); + const lastDameRow = Math.floor(lastDameFlatPos / this.modelSize); + const lastDameCol = lastDameFlatPos % this.modelSize; + + // 2. alle Damen kicken + // const resettedNDamesMatrix = this.nDamesMatrix.map((row) => { + // const resettedRow = row.map(() => false); + // return resettedRow; + // }); + // 3. assign cleared Matrix + // this.nDamesMatrix = resettedNDamesMatrix; + + // 3. Position auf Feld letzten Dame + this.modelPosition.row = lastDameRow; + this.modelPosition.col = lastDameCol; + + // 4. Wert resetten + this.nDamesMatrix[lastDameRow][lastDameCol] = false; + return this.modelPosition; }; } diff --git a/apps/n-dame-problem/src/n-dame-problem.controller.ts b/apps/n-dame-problem/src/n-dame-problem.controller.ts index 4005c36..d9afcad 100644 --- a/apps/n-dame-problem/src/n-dame-problem.controller.ts +++ b/apps/n-dame-problem/src/n-dame-problem.controller.ts @@ -19,12 +19,25 @@ export class NDameProblemController { @Get('/model') getModel( @Query('field', new DefaultValuePipe(-1), ParseIntPipe) field: number, + @Query('model-size', new DefaultValuePipe(5), ParseIntPipe) + modelSize: number, ): boolean[][] { - return this.nDameProblemService.getModel(field); + // wenn field gesetzt, dann hat das Prio + return this.nDameProblemService.getModel(field, modelSize); } @Get('/backtracking') getBacktracking() { return this.nDameProblemService.getBacktracking(); } + + @Get('/do-backtracking') + doBacktracking( + @Query('model-size', new DefaultValuePipe(5), ParseIntPipe) + modelSize: number, + ) { + return this.nDameProblemService + .doBacktracking(modelSize) + .map((row) => row.map((col) => (col ? 'X' : 'O'))); + } } diff --git a/apps/n-dame-problem/src/n-dame-problem.service.ts b/apps/n-dame-problem/src/n-dame-problem.service.ts index 1ccd78e..9c9b085 100644 --- a/apps/n-dame-problem/src/n-dame-problem.service.ts +++ b/apps/n-dame-problem/src/n-dame-problem.service.ts @@ -1,30 +1,48 @@ import { Injectable, Logger } from '@nestjs/common'; import { NDameModel } from './n-dame-model'; -import { NDameBacktracking } from './n-dame-backtracking'; +import { NDameBacktracker } from './n-dame-backtracker'; @Injectable() export class NDameProblemService { private readonly logger = new Logger(this.constructor.name); - private readonly MODEL_SIZE = 5; - getBacktracking(): NDameBacktracking { - const backTracking = new NDameBacktracking(4); + doBacktracking(modelSize: number): boolean[][] { + const nDameBacktracker = new NDameBacktracker(modelSize); - this.logger.log(`${backTracking.options()}`); - backTracking.solved(); - - return backTracking; + return nDameBacktracker.doBacktrack(); } - getModel(field: number): boolean[][] { - const model = new NDameModel(this.MODEL_SIZE); - const modelFieldCount = this.MODEL_SIZE * this.MODEL_SIZE; + getBacktracking(): NDameBacktracker { + const backTracker = new NDameBacktracker(5); + + backTracker.model.solved(); + + for (var i = 0; i < 5; i++) { + backTracker.model.nextPosition(); + backTracker.model.applyPosition(); + } + + backTracker.model.discardPosition(); + + backTracker.model.nextPosition(); + backTracker.model.applyPosition(); + + return backTracker; + } + + getModel(field: number, modelSize: number): boolean[][] { + const model = new NDameModel(modelSize); + const modelFieldCount = model.modelSize * model.modelSize; const rndFields = []; // geben wir keine FeldNr. an, dann die Zufallsreihe if (field === -1) - for (var i = 0; i < this.MODEL_SIZE; i++) - rndFields.push(Math.floor(Math.random() * modelFieldCount)); + for (var i = 0; i < model.modelSize; i++) { + var rndNr = Math.floor(Math.random() * modelFieldCount); + while (rndFields.includes(rndNr)) + rndNr = Math.floor(Math.random() * modelFieldCount); + rndFields.push(rndNr); + } else rndFields.push(field); this.logger.fatal(`[${rndFields.toString()}]`); diff --git a/libs/backtracking-lib/src/abstract-backtracker.ts b/libs/backtracking-lib/src/abstract-backtracker.ts new file mode 100644 index 0000000..0dd4f80 --- /dev/null +++ b/libs/backtracking-lib/src/abstract-backtracker.ts @@ -0,0 +1,55 @@ +import { Logger } from '@nestjs/common'; +import { IBacktrackingModel } from './backtracking-model.interface'; + +export abstract class ABacktracker { + protected readonly logger = new Logger(this.constructor.name); + + abstract doBacktrack: () => boolean[][]; + + protected findSolution = (dataModel: IBacktrackingModel): boolean => { + if (dataModel.solved()) return true; + + // nächstes freie Position suchen + // const nxtPossPos = dataModel.nextPossiblePosition(); + + while (dataModel.nextPossiblePosition()) { + dataModel.applyPosition(); + + // Lösung mit dem neu konfigurierten dataObject finden + const solved: boolean = this.findSolution(dataModel); + + if (solved) return true; + + const afterDiscardedPos = dataModel.discardPosition(); + this.logger.debug(`afterDiscardedPos: ${afterDiscardedPos}`); + } + + return false; + }; + + protected ___findSolution___ = (dataModel: IBacktrackingModel): boolean => { + if (dataModel.solved()) return true; + + // dataModel.options.forEach((option) => { + while (dataModel.hasNext()) { + // nächstes freie Position suchen + const nxtPos = dataModel.nextPosition(); + this.logger.debug(`nxtPos: ${nxtPos}`); + + // darf die Option hier eingestellt werden ? + if (dataModel.positionPossible()) { + dataModel.applyPosition(); + + // Lösung mit dem neu konfigurierten dataObject finden + const solved: boolean = this.findSolution(dataModel); + + if (solved) return true; + + const afterDiscardedPos = dataModel.discardPosition(); + this.logger.debug(`afterDiscardedPos: ${afterDiscardedPos}`); + } + } + + return false; + }; +} diff --git a/libs/backtracking-lib/src/backtracker.spec.ts b/libs/backtracking-lib/src/backtracker.spec.ts deleted file mode 100644 index fc3ee1e..0000000 --- a/libs/backtracking-lib/src/backtracker.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Backtracker } from './backtracker'; - -describe('Backtracker', () => { - it('should be defined', () => { - expect(new Backtracker()).toBeDefined(); - }); -}); diff --git a/libs/backtracking-lib/src/backtracker.ts b/libs/backtracking-lib/src/backtracker.ts deleted file mode 100644 index 0c7eb46..0000000 --- a/libs/backtracking-lib/src/backtracker.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IBacktracking } from './backtracking.interface'; - -export class Backtracker { - findSolution = (dataObject: IBacktracking): boolean => { - if (dataObject.solved()) return true; - - // nächstes freie Position suchen - dataObject.nextPosition(); - - dataObject.options().forEach((option) => { - // darf die Option hier eingestellt werden ? - if (dataObject.positionPossible()) { - dataObject.applyPosition(); - - // Lösung mit dem neu konfigurierten dataObject finden - const solved: boolean = this.findSolution(dataObject); - - if (solved) return true; - - dataObject.discardPosition(); - } - }); - - return false; - }; -} diff --git a/libs/backtracking-lib/src/backtracking.interface.ts b/libs/backtracking-lib/src/backtracking-model.interface.ts similarity index 69% rename from libs/backtracking-lib/src/backtracking.interface.ts rename to libs/backtracking-lib/src/backtracking-model.interface.ts index 873a52c..8980b6c 100644 --- a/libs/backtracking-lib/src/backtracking.interface.ts +++ b/libs/backtracking-lib/src/backtracking-model.interface.ts @@ -1,9 +1,10 @@ import { MatrixPosition } from './matrix-position'; -export interface IBacktracking { - options: () => Array; +export interface IBacktrackingModel { solved: () => boolean; + hasNext: () => boolean; nextPosition: () => MatrixPosition; + nextPossiblePosition: () => boolean; positionPossible: () => boolean; applyPosition: () => MatrixPosition; discardPosition: () => MatrixPosition; diff --git a/package.json b/package.json index 3b59341..513786b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "backtracking", - "version": "1.0.0", + "version": "2.0.0", "description": "", "author": "", "private": true,