v.1.0.0 NDameModel setup

This commit is contained in:
TiiJay
2024-11-29 11:05:41 +01:00
parent c8ef9e7a2c
commit b4ee271fff
10 changed files with 368 additions and 4 deletions

View File

@@ -0,0 +1,22 @@
import { NDameModel } from './n-dame-model';
export class NDameBacktracking {
private model: NDameModel;
constructor(private readonly modelSize: number) {
this.model = new NDameModel(modelSize);
}
options = (): Array<number> => {
// 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();
};
}

View File

@@ -0,0 +1,192 @@
import { IBacktracking } from '@app/backtracking-lib/backtracking.interface';
import { MatrixPosition } from '@app/backtracking-lib/matrix-position';
import { Logger } from '@nestjs/common';
enum EDiagonal {
NW_SO = 1,
NO_SW = -1,
}
export class NDameModel implements IBacktracking {
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,
);
constructor(private readonly modelSize: number) {
this.initNDameField();
}
private initNDameField() {
for (var zeile = 0; zeile < this.modelSize; zeile++) {
this.nDamesMatrix[zeile] = [];
for (var spalte = 0; spalte < this.modelSize; spalte++) {
this.nDamesMatrix[zeile].push(false);
}
}
}
public get matrix(): boolean[][] {
return this.nDamesMatrix;
}
// getter: Anzahl der gesetzten Damen
private get dames(): number {
const arrOfMatrix = this.nDamesMatrix.flat();
const dames = arrOfMatrix.reduce((a, b: boolean) => (b ? a + 1 : a), 0);
return dames;
}
// sind alle N Damen auf dem Feld platziert ?
public solved = (): boolean => {
return this.dames == this.modelSize;
};
options = (): Array<number> => {
// wir können nur 1 Dame pro Feld platzieren !!!
return [1];
// const optionsArray = Array.from(
// { length: this.modelSize },
// (value, index) => index,
// );
};
nextPosition = () => {
this.modelPosition.col++;
if (this.modelPosition.col == this.modelSize) {
this.modelPosition.row++;
this.modelPosition.col = 0;
}
if (this.modelPosition.row == this.modelSize)
throw new Error(`model bounds crashed...\n${this.modelPosition}`);
return this.modelPosition;
};
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}`);
return damesInRow > 1;
};
private colProhibited = (): boolean => {
const damesCol = this.nDamesMatrix.reduce((acc, act) => {
acc.push(act[this.modelPosition.col]);
return acc;
}, []);
// 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}`);
return damesInCol > 1;
};
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;
var found = false;
while (!found) {
if (startPos.row == 0 || startPos.col == colSearchEndPos) found = true;
else {
--startPos.row;
startPos.col += -diagDirection;
}
}
var diagReady = false;
var diagPos = startPos;
const diagCoords = [];
const damesDiag = [];
const colExitPos = diagDirection == EDiagonal.NW_SO ? this.modelSize : -1;
while (!diagReady) {
if (diagPos.row == this.modelSize || diagPos.col == colExitPos)
diagReady = true;
else {
diagCoords.push(new MatrixPosition(diagPos.row, diagPos.col));
damesDiag.push(this.nDamesMatrix[diagPos.row][diagPos.col]);
++diagPos.row;
diagPos.col += diagDirection;
}
}
this.logger.verbose(diagCoords.join());
return damesDiag;
};
private diagonalsProhibited = (): boolean => {
const diagNW_SO = this.damesInDiagonal(EDiagonal.NW_SO);
const diagNO_SW = this.damesInDiagonal(EDiagonal.NO_SW);
// steht schon eine Dame in der Diagonale NW_SO ?
const damesInDiagonalNW_SO = diagNW_SO.reduce(
(acc, act) => (act ? ++acc : acc),
0,
);
// steht schon eine Dame in der Diagonale NW_SO ?
const damesInDiagonalNO_SW = diagNO_SW.reduce(
(acc, act) => (act ? ++acc : acc),
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}`);
return damesInDiagonalNW_SO > 1 || damesInDiagonalNO_SW > 1;
};
// darf hier eine Dame stehen ?
positionPossible = (): boolean => {
const rp = this.rowProhibited();
const cp = this.colProhibited();
const dp = this.diagonalsProhibited();
const positionOk = !rp && !cp && !dp;
if (positionOk)
this.logger.log(`free: ${JSON.stringify(this.modelPosition)}`);
return positionOk;
};
// Dame auf das Feld platzieren
applyPosition = (): MatrixPosition => {
this.logger.warn(`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;
return this.modelPosition;
};
}

View File

@@ -1,12 +1,30 @@
import { Controller, Get } from '@nestjs/common'; import {
Controller,
DefaultValuePipe,
Get,
ParseIntPipe,
Query,
} from '@nestjs/common';
import { NDameProblemService } from './n-dame-problem.service'; import { NDameProblemService } from './n-dame-problem.service';
@Controller() @Controller()
export class NDameProblemController { export class NDameProblemController {
constructor(private readonly nDameProblemService: NDameProblemService) {} constructor(private readonly nDameProblemService: NDameProblemService) {}
@Get() @Get('/hello')
getHello(): string { getHello(): string {
return this.nDameProblemService.getHello(); return this.nDameProblemService.getHello();
} }
@Get('/model')
getModel(
@Query('field', new DefaultValuePipe(-1), ParseIntPipe) field: number,
): boolean[][] {
return this.nDameProblemService.getModel(field);
}
@Get('/backtracking')
getBacktracking() {
return this.nDameProblemService.getBacktracking();
}
} }

View File

@@ -1,7 +1,48 @@
import { Injectable } from '@nestjs/common'; import { Injectable, Logger } from '@nestjs/common';
import { NDameModel } from './n-dame-model';
import { NDameBacktracking } from './n-dame-backtracking';
@Injectable() @Injectable()
export class NDameProblemService { export class NDameProblemService {
private readonly logger = new Logger(this.constructor.name);
private readonly MODEL_SIZE = 5;
getBacktracking(): NDameBacktracking {
const backTracking = new NDameBacktracking(4);
this.logger.log(`${backTracking.options()}`);
backTracking.solved();
return backTracking;
}
getModel(field: number): boolean[][] {
const model = new NDameModel(this.MODEL_SIZE);
const modelFieldCount = this.MODEL_SIZE * this.MODEL_SIZE;
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));
else rndFields.push(field);
this.logger.fatal(`[${rndFields.toString()}]`);
for (var i = 0; i < modelFieldCount; i++) {
const nxtPos = model.nextPosition();
if (rndFields.includes(i)) {
model.applyPosition();
model.positionPossible();
}
// this.logger.verbose(`${i + 1} - ${JSON.stringify(nxtPos)}`);
}
return model.matrix;
}
getHello(): string { getHello(): string {
return 'Hello World!'; return 'Hello World!';
} }

View File

@@ -0,0 +1,7 @@
import { Backtracker } from './backtracker';
describe('Backtracker', () => {
it('should be defined', () => {
expect(new Backtracker()).toBeDefined();
});
});

View File

@@ -0,0 +1,26 @@
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;
};
}

View File

@@ -1,4 +1,27 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
/**
* PSEUDO-CODE:
*
void FIND_SOLUTIONS (Parameter):
wenn (gültige Lösung ):
Bewahren Sie die Lösung auf
Zurückkehren (gelöst)
für (alle Auswahlmöglichkeiten ):
wenn (gültige Auswahl ):
ANWENDEN ( Auswahl )
FIND_SOLUTIONS (Parameter)
wenn (gültige Lösung ):
Bewahren Sie die Lösung auf
Zurückkehren (gelöst)
BACKTRACK ( Auswahl entfernen )
Zurückkehren (nicht gelöst)
*/
@Injectable() @Injectable()
export class BacktrackingLibService {} export class BacktrackingLibService {}

View File

@@ -0,0 +1,10 @@
import { MatrixPosition } from './matrix-position';
export interface IBacktracking {
options: () => Array<any>;
solved: () => boolean;
nextPosition: () => MatrixPosition;
positionPossible: () => boolean;
applyPosition: () => MatrixPosition;
discardPosition: () => MatrixPosition;
}

View File

@@ -0,0 +1,25 @@
export class MatrixPosition {
constructor(
private _row: number,
private _col: number,
) {}
public get row() {
return this._row;
}
public set row(r: number) {
this._row = r;
}
public get col() {
return this._col;
}
public set col(c: number) {
this._col = c;
}
public toString() {
return `[${this._row},${this._col}]`;
}
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "backtracking", "name": "backtracking",
"version": "0.9.0", "version": "1.0.0",
"description": "", "description": "",
"author": "", "author": "",
"private": true, "private": true,