mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 00:01:37 +08:00
feat: Test running overhaul, switch to Prettier & reformat everything (#1407)
* chore: Switch to Node 20 + Vitest * chore: migrate to vitest mock functions * chore: code style (switch to prettier) * test: re-enable long-running test Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime! see #1193 * chore: code style * chore: fix failing tests * Updated Documentation in README.md * Update contribution guidelines to state usage of Prettier * fix: set prettier printWidth back to 80 * chore: apply updated code style automatically * fix: set prettier line endings to lf again * chore: apply updated code style automatically --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
function euclideanGCDRecursive (first, second) {
|
||||
function euclideanGCDRecursive(first, second) {
|
||||
/*
|
||||
Calculates GCD of two numbers using Euclidean Recursive Algorithm
|
||||
:param first: First number
|
||||
@ -8,11 +8,11 @@ function euclideanGCDRecursive (first, second) {
|
||||
if (second === 0) {
|
||||
return first
|
||||
} else {
|
||||
return euclideanGCDRecursive(second, (first % second))
|
||||
return euclideanGCDRecursive(second, first % second)
|
||||
}
|
||||
}
|
||||
|
||||
function euclideanGCDIterative (first, second) {
|
||||
function euclideanGCDIterative(first, second) {
|
||||
/*
|
||||
Calculates GCD of two numbers using Euclidean Iterative Algorithm
|
||||
:param first: First number
|
||||
|
@ -1,103 +1,132 @@
|
||||
/**
|
||||
* Flood fill.
|
||||
*
|
||||
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
|
||||
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
|
||||
* connected, similarly-colored areas with a different color.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
|
||||
* @see https://www.techiedelight.com/flood-fill-algorithm/
|
||||
*/
|
||||
|
||||
const neighbors = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a breadth-first approach using a queue.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function breadthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
if (location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
location[1] >= rgbData[0].length) {
|
||||
throw new Error('location should point to a pixel within the rgbData')
|
||||
}
|
||||
|
||||
const queue = []
|
||||
queue.push(location)
|
||||
|
||||
while (queue.length > 0) {
|
||||
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a depth-first approach using recursion.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function depthFirstSearch (rgbData, location, targetColor, replacementColor) {
|
||||
if (location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
location[1] >= rgbData[0].length) {
|
||||
throw new Error('location should point to a pixel within the rgbData')
|
||||
}
|
||||
|
||||
depthFirstFill(rgbData, location, targetColor, replacementColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the breadth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param queue The locations that still need to be visited.
|
||||
*/
|
||||
function breadthFirstFill (rgbData, location, targetColor, replacementColor, queue) {
|
||||
const currentLocation = queue[0]
|
||||
queue.shift()
|
||||
|
||||
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
|
||||
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
|
||||
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const x = currentLocation[0] + neighbors[i][0]
|
||||
const y = currentLocation[1] + neighbors[i][1]
|
||||
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
|
||||
queue.push([x, y])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the depth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
function depthFirstFill (rgbData, location, targetColor, replacementColor) {
|
||||
if (rgbData[location[0]][location[1]] === targetColor) {
|
||||
rgbData[location[0]][location[1]] = replacementColor
|
||||
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const x = location[0] + neighbors[i][0]
|
||||
const y = location[1] + neighbors[i][1]
|
||||
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
|
||||
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Flood fill.
|
||||
*
|
||||
* Flood fill, also called seed fill, is an algorithm that determines and alters the area connected to a given node in a
|
||||
* multi-dimensional array with some matching attribute. It is used in the "bucket" fill tool of paint programs to fill
|
||||
* connected, similarly-colored areas with a different color.
|
||||
*
|
||||
* (description adapted from https://en.wikipedia.org/wiki/Flood_fill)
|
||||
* @see https://www.techiedelight.com/flood-fill-algorithm/
|
||||
*/
|
||||
|
||||
const neighbors = [
|
||||
[-1, -1],
|
||||
[-1, 0],
|
||||
[-1, 1],
|
||||
[0, -1],
|
||||
[0, 1],
|
||||
[1, -1],
|
||||
[1, 0],
|
||||
[1, 1]
|
||||
]
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a breadth-first approach using a queue.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function breadthFirstSearch(
|
||||
rgbData,
|
||||
location,
|
||||
targetColor,
|
||||
replacementColor
|
||||
) {
|
||||
if (
|
||||
location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
location[1] >= rgbData[0].length
|
||||
) {
|
||||
throw new Error('location should point to a pixel within the rgbData')
|
||||
}
|
||||
|
||||
const queue = []
|
||||
queue.push(location)
|
||||
|
||||
while (queue.length > 0) {
|
||||
breadthFirstFill(rgbData, location, targetColor, replacementColor, queue)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the flood fill algorithm through a depth-first approach using recursion.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
export function depthFirstSearch(
|
||||
rgbData,
|
||||
location,
|
||||
targetColor,
|
||||
replacementColor
|
||||
) {
|
||||
if (
|
||||
location[0] < 0 ||
|
||||
location[0] >= rgbData.length ||
|
||||
location[1] < 0 ||
|
||||
location[1] >= rgbData[0].length
|
||||
) {
|
||||
throw new Error('location should point to a pixel within the rgbData')
|
||||
}
|
||||
|
||||
depthFirstFill(rgbData, location, targetColor, replacementColor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the breadth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
* @param queue The locations that still need to be visited.
|
||||
*/
|
||||
function breadthFirstFill(
|
||||
rgbData,
|
||||
location,
|
||||
targetColor,
|
||||
replacementColor,
|
||||
queue
|
||||
) {
|
||||
const currentLocation = queue[0]
|
||||
queue.shift()
|
||||
|
||||
if (rgbData[currentLocation[0]][currentLocation[1]] === targetColor) {
|
||||
rgbData[currentLocation[0]][currentLocation[1]] = replacementColor
|
||||
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const x = currentLocation[0] + neighbors[i][0]
|
||||
const y = currentLocation[1] + neighbors[i][1]
|
||||
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
|
||||
queue.push([x, y])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility-function to implement the depth-first loop.
|
||||
*
|
||||
* @param rgbData The image to which the algorithm is applied.
|
||||
* @param location The start location on the image.
|
||||
* @param targetColor The old color to be replaced.
|
||||
* @param replacementColor The new color to replace the old one.
|
||||
*/
|
||||
function depthFirstFill(rgbData, location, targetColor, replacementColor) {
|
||||
if (rgbData[location[0]][location[1]] === targetColor) {
|
||||
rgbData[location[0]][location[1]] = replacementColor
|
||||
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
const x = location[0] + neighbors[i][0]
|
||||
const y = location[1] + neighbors[i][1]
|
||||
if (x >= 0 && x < rgbData.length && y >= 0 && y < rgbData[0].length) {
|
||||
depthFirstFill(rgbData, [x, y], targetColor, replacementColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
/** Class to handle the vector calculations. */
|
||||
export class Vector2 {
|
||||
constructor (x, y) {
|
||||
constructor(x, y) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
}
|
||||
@ -26,7 +26,7 @@ export class Vector2 {
|
||||
* @param vector The vector to be added.
|
||||
* @returns The sum-vector.
|
||||
*/
|
||||
add (vector) {
|
||||
add(vector) {
|
||||
const x = this.x + vector.x
|
||||
const y = this.y + vector.y
|
||||
return new Vector2(x, y)
|
||||
@ -38,7 +38,7 @@ export class Vector2 {
|
||||
* @param vector The vector to be subtracted.
|
||||
* @returns The difference-vector.
|
||||
*/
|
||||
subtract (vector) {
|
||||
subtract(vector) {
|
||||
const x = this.x - vector.x
|
||||
const y = this.y - vector.y
|
||||
return new Vector2(x, y)
|
||||
@ -50,7 +50,7 @@ export class Vector2 {
|
||||
* @param scalar The factor by which to multiply the vector.
|
||||
* @returns The scaled vector.
|
||||
*/
|
||||
multiply (scalar) {
|
||||
multiply(scalar) {
|
||||
const x = this.x * scalar
|
||||
const y = this.y * scalar
|
||||
return new Vector2(x, y)
|
||||
@ -62,8 +62,8 @@ export class Vector2 {
|
||||
* @param angleInDegrees The angle by which to rotate the vector.
|
||||
* @returns The rotated vector.
|
||||
*/
|
||||
rotate (angleInDegrees) {
|
||||
const radians = angleInDegrees * Math.PI / 180
|
||||
rotate(angleInDegrees) {
|
||||
const radians = (angleInDegrees * Math.PI) / 180
|
||||
const ca = Math.cos(radians)
|
||||
const sa = Math.sin(radians)
|
||||
const x = ca * this.x - sa * this.y
|
||||
@ -81,7 +81,7 @@ export class Vector2 {
|
||||
* @param steps The number of iterations.
|
||||
* @returns The transformed vectors after the iteration-steps.
|
||||
*/
|
||||
export function iterate (initialVectors, steps) {
|
||||
export function iterate(initialVectors, steps) {
|
||||
let vectors = initialVectors
|
||||
for (let i = 0; i < steps; i++) {
|
||||
vectors = iterationStep(vectors)
|
||||
@ -99,7 +99,7 @@ export function iterate (initialVectors, steps) {
|
||||
* @param vectors The vectors composing the shape to which the algorithm is applied.
|
||||
* @returns The transformed vectors after the iteration-step.
|
||||
*/
|
||||
function iterationStep (vectors) {
|
||||
function iterationStep(vectors) {
|
||||
const newVectors = []
|
||||
for (let i = 0; i < vectors.length - 1; i++) {
|
||||
const startVector = vectors[i]
|
||||
@ -107,7 +107,9 @@ function iterationStep (vectors) {
|
||||
newVectors.push(startVector)
|
||||
const differenceVector = endVector.subtract(startVector).multiply(1 / 3)
|
||||
newVectors.push(startVector.add(differenceVector))
|
||||
newVectors.push(startVector.add(differenceVector).add(differenceVector.rotate(60)))
|
||||
newVectors.push(
|
||||
startVector.add(differenceVector).add(differenceVector.rotate(60))
|
||||
)
|
||||
newVectors.push(startVector.add(differenceVector.multiply(2)))
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { Vector2, iterate } from './KochSnowflake'
|
||||
* @param steps The number of iterations.
|
||||
* @returns The canvas of the rendered Koch snowflake.
|
||||
*/
|
||||
function getKochSnowflake (canvasWidth = 600, steps = 5) {
|
||||
function getKochSnowflake(canvasWidth = 600, steps = 5) {
|
||||
if (canvasWidth <= 0) {
|
||||
throw new Error('canvasWidth should be greater than zero')
|
||||
}
|
||||
@ -15,7 +15,10 @@ function getKochSnowflake (canvasWidth = 600, steps = 5) {
|
||||
const offsetX = canvasWidth / 10.0
|
||||
const offsetY = canvasWidth / 3.7
|
||||
const vector1 = new Vector2(offsetX, offsetY)
|
||||
const vector2 = new Vector2(canvasWidth / 2, Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY)
|
||||
const vector2 = new Vector2(
|
||||
canvasWidth / 2,
|
||||
Math.sin(Math.PI / 3) * canvasWidth * 0.8 + offsetY
|
||||
)
|
||||
const vector3 = new Vector2(canvasWidth - offsetX, offsetY)
|
||||
const initialVectors = []
|
||||
initialVectors.push(vector1)
|
||||
@ -34,7 +37,7 @@ function getKochSnowflake (canvasWidth = 600, steps = 5) {
|
||||
* @param canvasHeight The height of the canvas.
|
||||
* @returns The canvas of the rendered edges.
|
||||
*/
|
||||
function drawToCanvas (vectors, canvasWidth, canvasHeight) {
|
||||
function drawToCanvas(vectors, canvasWidth, canvasHeight) {
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.width = canvasWidth
|
||||
canvas.height = canvasHeight
|
||||
|
@ -1,7 +1,7 @@
|
||||
// wiki - https://en.wikipedia.org/wiki/Tower_of_Hanoi
|
||||
// Recursive Javascript function to solve tower of hanoi
|
||||
|
||||
export function TowerOfHanoi (n, from, to, aux, output = []) {
|
||||
export function TowerOfHanoi(n, from, to, aux, output = []) {
|
||||
if (n === 1) {
|
||||
output.push(`Move disk 1 from rod ${from} to rod ${to}`)
|
||||
return output
|
||||
|
@ -30,7 +30,12 @@ describe('FloodFill', () => {
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation.
|
||||
*/
|
||||
function testBreadthFirst (fillLocation, targetColor, replacementColor, testLocation) {
|
||||
function testBreadthFirst(
|
||||
fillLocation,
|
||||
targetColor,
|
||||
replacementColor,
|
||||
testLocation
|
||||
) {
|
||||
const rgbData = generateTestRgbData()
|
||||
breadthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
@ -45,7 +50,13 @@ function testBreadthFirst (fillLocation, targetColor, replacementColor, testLoca
|
||||
* @param testLocation The location of the color to be checked.
|
||||
* @return The color at testLocation.
|
||||
*/
|
||||
function testDepthFirst (fillLocation, targetColor, replacementColor, testLocation) {// eslint-disable-line
|
||||
function testDepthFirst(
|
||||
fillLocation,
|
||||
targetColor,
|
||||
replacementColor,
|
||||
testLocation
|
||||
) {
|
||||
// eslint-disable-line
|
||||
const rgbData = generateTestRgbData()
|
||||
depthFirstSearch(rgbData, fillLocation, targetColor, replacementColor)
|
||||
return rgbData[testLocation[0]][testLocation[1]]
|
||||
@ -56,7 +67,7 @@ function testDepthFirst (fillLocation, targetColor, replacementColor, testLocati
|
||||
*
|
||||
* @return example rgbData-matrix.
|
||||
*/
|
||||
function generateTestRgbData () {
|
||||
function generateTestRgbData() {
|
||||
const layout = [
|
||||
[violet, violet, green, green, black, green, green],
|
||||
[violet, green, green, black, green, green, green],
|
||||
|
@ -2,19 +2,29 @@ import { iterate, Vector2 } from '../KochSnowflake'
|
||||
|
||||
describe('KochSnowflake', () => {
|
||||
it('should produce the correctly-transformed vectors', () => {
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0])
|
||||
.toEqual({ x: 0, y: 0 })
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[0]).toEqual({
|
||||
x: 0,
|
||||
y: 0
|
||||
})
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1])
|
||||
.toEqual({ x: 1 / 3, y: 0 })
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[1]).toEqual({
|
||||
x: 1 / 3,
|
||||
y: 0
|
||||
})
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2])
|
||||
.toEqual({ x: 1 / 2, y: Math.sin(Math.PI / 3) / 3 })
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[2]).toEqual({
|
||||
x: 1 / 2,
|
||||
y: Math.sin(Math.PI / 3) / 3
|
||||
})
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3])
|
||||
.toEqual({ x: 2 / 3, y: 0 })
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[3]).toEqual({
|
||||
x: 2 / 3,
|
||||
y: 0
|
||||
})
|
||||
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4])
|
||||
.toEqual({ x: 1, y: 0 })
|
||||
expect(iterate([new Vector2(0, 0), new Vector2(1, 0)], 1)[4]).toEqual({
|
||||
x: 1,
|
||||
y: 0
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user