Best JavaScript code snippet using cypress
Beam.js
Source:Beam.js
1import { create, all } from 'mathjs'2const math = create(all)3export default class Beam {4 constructor () {5 this._length = null6 this._moment = null7 this._modulus = null8 this.pointLoads = [] // Array of discrete loads. (Note the absence of the _ in this assignment: this invokes the setter which creates a Proxy; see the setter below for more details.)9 this._contLoad = () => 0 // A function that gives the load on the beam as a function of distance from the left anchor10 this._anchor = ['free', 'free'] // or 'fixed'11 this._isSolved = false12 this.pins = [] // pins and rollers are the same for this thing13 }14 get length () {15 return this._length16 }17 set length (newLength) {18 if (typeof newLength !== 'number' || !(newLength > 0)) {19 throw new TypeError('length must be a positive number')20 }21 this._length = newLength22 this._isSolved = false23 }24 get moment () {25 return this._moment26 }27 set moment (newMoment) {28 if (newMoment < 0 || (typeof newMoment !== 'number' && typeof newMoment !== 'function')) {29 throw new TypeError('moment must be a positive number or a function that returns the moment as a function of the distance from the left end of the beam')30 }31 if (typeof newMoment === 'function') {32 throw new TypeError('Support for moment as a function is not available yet but is planned for the future.')33 }34 this._moment = newMoment35 this._isSolved = false36 }37 get modulus () {38 return this._modulus39 }40 set modulus (newModulus) {41 if (newModulus < 0 || typeof newModulus !== 'number') {42 throw new TypeError('modulus must be a positive number')43 }44 this._modulus = newModulus45 this._isSolved = false46 }47 get contLoad () {48 return this._contLoad49 }50 set contLoad (newContLoad) {51 if (typeof newContLoad !== 'function') {52 throw new TypeError('contLoad must be a function that returns the load density as a function of the distance from the left end of the beam')53 }54 this._contLoad = newContLoad55 this._isSolved = false56 }57 get anchorLeft () {58 return this._anchor[0]59 }60 set anchorLeft (newAnchorLeft) {61 const allowed = ['free', 'fixed']62 if (!allowed.includes(newAnchorLeft)) {63 throw new TypeError(`anchorLeft must be one of ${allowed.join(', ')}`)64 }65 this._anchor[0] = newAnchorLeft66 this._isSolved = false67 }68 get anchorRight () {69 return this._anchor[1]70 }71 set anchorRight (newAnchorRight) {72 const allowed = ['free', 'fixed']73 if (!allowed.includes(newAnchorRight)) {74 throw new TypeError(`anchorRight must be one of ${allowed.join(', ')}`)75 }76 this._anchor[1] = newAnchorRight77 this._isSolved = false78 }79 get pointLoads () {80 return this._pointLoadsProxy81 }82 set pointLoads (newPointLoads) {83 if (!Array.isArray(newPointLoads)) {84 throw new TypeError('pointLoads must be an array')85 }86 // Check to make sure each item in newPointLoads is acceptable87 for (let ptLoad of newPointLoads) {88 if (!this._isValidPointLoad(ptLoad)) {89 throw new TypeError('Each item of pointLoads must be an object of type: { x: number, w: number }')90 }91 }92 this._pointLoads = newPointLoads93 // Proxy _pointLoads so we're notified if the user modifies it94 const arrayHandler = {95 set: (target, property, value) => {96 // console.log(`Setting property ${property} of ${JSON.stringify(target)} to ${JSON.stringify(value)}`)97 if (/^\d+$/.test(property)) {98 // Array indexing99 if (!this._isValidPointLoad(value)) {100 throw new TypeError('A point load must be an object of type: { x: number, w: number }')101 }102 Object.freeze(value)103 }104 target[property] = value105 this._isSolved = false106 return Reflect.set(target, property, value)107 }108 }109 this._pointLoadsProxy = new Proxy(this._pointLoads, arrayHandler)110 this._isSolved = false111 }112 /**113 * Tests whether the given object is a valid point load.114 * @param {Object} obj The point load to test for validity115 */116 _isValidPointLoad (obj) {117 return obj && typeof obj.x === 'number' && typeof obj.w === 'number'118 }119 /**120 * Adds a point load to the beam, and returns the new point load that was added.121 * @param {number} x The distance from the left end of the beam to add the point load.122 * @param {number} w The downward force of the point load.123 * @returns {Object} The new point load that was added.124 */125 addPointLoad (x, w) {126 let newPointLoad127 if (typeof x === 'object') {128 newPointLoad = x129 } else if (typeof x === 'number' && typeof w === 'number') {130 newPointLoad = { x, w }131 }132 if (!this._isValidPointLoad(newPointLoad)) {133 throw new TypeError('You must supply two numbers, or an object containing properties x and w which are both numbers')134 }135 Object.freeze(newPointLoad)136 this._pointLoads.push(newPointLoad)137 this._isSolved = false138 return newPointLoad139 }140 /**141 * Removes a point load that was added using addPointLoad.142 * @param {Object} pointLoad143 */144 removePointLoad (pointLoad) {145 const i = this._pointLoads.indexOf(pointLoad)146 if (i < 0) {147 // TODO: Should we return true/false indicating whether the point load was found, instead of throwing?148 throw new Error('The given point load was not found. (Point loads are matched by reference, not value.)')149 }150 this._pointLoads.splice(i, 1)151 this._isSolved = false152 return true153 }154 get pins () {155 return this._pinsProxy156 }157 set pins (newPins) {158 if (!Array.isArray(newPins)) {159 throw new TypeError('pins must be an array')160 }161 // Check to make sure each item in newPins is acceptable162 for (let pin of newPins) {163 if (!this._isValidPin(pin)) {164 throw new TypeError('Each item of pins must be an object with a single property `x` of type number')165 }166 }167 this._pins = newPins168 // Proxy _pins so we're notified if the user modifies it169 const arrayHandler = {170 set: (target, property, value) => {171 // console.log(`Setting property ${property} of ${JSON.stringify(target)} to ${JSON.stringify(value)}`)172 if (/^\d+$/.test(property)) {173 // Array indexing174 if (!this._isValidPin(value)) {175 throw new TypeError('A pin must be an object with a single property `x` of type number')176 }177 Object.freeze(value)178 }179 target[property] = value180 this._isSolved = false181 return Reflect.set(target, property, value)182 }183 }184 this._pinsProxy = new Proxy(this._pins, arrayHandler)185 this._isSolved = false186 }187 /**188 * Tests whether the given object is a valid pin.189 * @param {Object} obj The pin to test for validity190 */191 _isValidPin (obj) {192 return obj && typeof obj.x === 'number'193 }194 /**195 * Adds a pin to the beam, and returns the pin that was added.196 * @param {number} x The distance from the left end of the beam to add the point load.197 * @returns {Object} The new pin that was added.198 */199 addPin (x) {200 let newPin201 if (typeof x === 'object') {202 newPin = x203 } else if (typeof x === 'number') {204 newPin = { x }205 }206 if (!this._isValidPin(newPin)) {207 throw new TypeError('You must supply a number, or an object containing a single property x which is a number')208 }209 Object.freeze(newPin)210 this._pins.push(newPin)211 this._isSolved = false212 return newPin213 }214 /**215 * Removes a pin that was added using addPin.216 * @param {Object} pin217 */218 removePin (pin) {219 const i = this._pins.indexOf(pin)220 if (i < 0) {221 // TODO: Should we return true/false indicating whether the pin was found, instead of throwing?222 throw new Error('The given pin was not found. (Pins are matched by reference, not value.)')223 }224 this._pins.splice(i, 1)225 this._isSolved = false226 return true227 }228 /**229 * Creates an array of evenly spaced points, plus two points for each point load, which are used to solve the beam deflection problem.230 * @param {number} numGridPts The number of evenly spaced intervals to create. The actual number of grid points is one more than this number (endpoints are included). Two additional grid points are also created for every point load.231 * @returns {Array} An array of grid points sorted by x-coordinate.232 */233 _createGrid (numGridPts) {234 if (typeof numGridPts !== 'number' || !(numGridPts > 0) || Math.round(numGridPts) !== numGridPts) {235 throw new TypeError('numGridPts must be a positive integer.')236 }237 // Create a grid of points238 const grid = []239 // Add evenly spaced points240 for (let i = 0; i <= numGridPts; i++) {241 grid[i] = {242 x: this._length * (i / numGridPts) // parens ensure that the final x will be exactly _length243 }244 // grid[i] = { }245 // grid[i].x = this._length * i / numGridPts246 }247 // Add two grid points for each point load248 for (let ptLoad of this._pointLoads) {249 // NOTE: The conditional statements in this block make exact comparisons between floating-point numbers. This is intended.250 // Check for ptLoads that exist at an existing grid location251 let dupeGridPointIndex = grid.findIndex(pt => pt.x === ptLoad.x && !pt.isPointLoad && !pt.isPin)252 if (dupeGridPointIndex >= 0) {253 // Remove the existing grid point254 grid.splice(dupeGridPointIndex, 1)255 }256 // Check for duplicate ptLoads257 let dupePointLoadIndex1 = grid.findIndex(pt => pt.x === ptLoad.x && pt.isPointLoad && pt.relationToFeature === -1)258 let dupePointLoadIndex2 = grid.findIndex(pt => pt.x === ptLoad.x && pt.isPointLoad && pt.relationToFeature === 1)259 if (dupePointLoadIndex1 >= 0 && dupePointLoadIndex2 >= 0) {260 // Instead of adding new grid points, just add this point load to the existing ones261 grid[dupePointLoadIndex1].pointLoad += ptLoad.w262 grid[dupePointLoadIndex2].pointLoad += ptLoad.w263 } else {264 // Add two new grid points for this point load265 grid.push({266 x: ptLoad.x,267 pointLoad: ptLoad.w,268 isPointLoad: true,269 relationToFeature: -1270 })271 grid.push({272 x: ptLoad.x,273 pointLoad: ptLoad.w,274 isPointLoad: true,275 relationToFeature: 1276 })277 }278 }279 // Add two grid points for each pin280 for (let pin of this._pins) {281 // Check for duplicate pin at this location282 if (grid.some(pt => pt.x === pin.x && pt.isPin)) {283 // No need to add a second pin at the same point284 continue285 }286 // Check for pin at existing grid point (but not a point load)287 let dupeGridPointIndex = grid.findIndex(pt => pt.x === pin.x && !pt.isPointLoad && !pt.isPin)288 if (dupeGridPointIndex >= 0) {289 // Remove the existing grid point290 grid.splice(dupeGridPointIndex, 1)291 }292 // Check for point load at same location293 let dupePointLoadIndex1 = grid.findIndex(pt => pt.x === pin.x && pt.isPointLoad && pt.relationToFeature === -1)294 let dupePointLoadIndex2 = grid.findIndex(pt => pt.x === pin.x && pt.isPointLoad && pt.relationToFeature === 1)295 if (dupePointLoadIndex1 >= 0 && dupePointLoadIndex2 >= 0) {296 // Instead of adding new grid points, just mark this point as a pin297 grid[dupePointLoadIndex1].isPin = true298 grid[dupePointLoadIndex2].isPin = true299 } else {300 // Add two new grid points for this pin301 grid.push({302 x: pin.x,303 isPin: true,304 relationToFeature: -1305 })306 grid.push({307 x: pin.x,308 isPin: true,309 relationToFeature: 1310 })311 }312 }313 // If anchors are fixed, add a second grid point since there will be a discontinuity at the endpoints314 if (this.anchorLeft === 'fixed') {315 let existingGridPt = grid.findIndex(pt => pt.x === 0 && !pt.isPointLoad && !pt.isPin)316 if (existingGridPt >= 0) {317 // Add second grid point318 grid.splice(existingGridPt, 1, {319 x: 0,320 isFixedAnchor: true,321 relationToFeature: -1322 }, {323 x: 0,324 isFixedAnchor: true,325 relationToFeature: 1326 })327 }328 }329 if (this.anchorRight === 'fixed') {330 let existingGridPt = grid.findIndex(pt => pt.x === this._length && !pt.isPointLoad && !pt.isPin)331 if (existingGridPt >= 0) {332 // Add second grid point333 grid.splice(existingGridPt, 1, {334 x: this._length,335 isFixedAnchor: true,336 relationToFeature: -1337 }, {338 x: this._length,339 isFixedAnchor: true,340 relationToFeature: 1341 })342 }343 }344 // Sort grid first by x-coordinate, then by relationToPointLoad345 // TODO: Use binary search insertion in the for..of loop above to improve performance346 grid.sort((a, b) => {347 if (a.x > b.x) return 1348 else if (a.x < b.x) return -1349 else if (a.relationToPointLoad > b.relationToPointLoad) return 1350 else if (a.relationToPointLoad < b.relationToPointLoad) return -1351 else if (a.relationToPin > b.relationToPin) return 1352 else if (a.relationToPin < b.relationToPin) return -1353 else return 0354 })355 return grid356 }357 /** Returns the grid point(s) that whose x-coordinate is equal to x. If x is at a pin, there could be multiple grid points matching x. */358 _findGridPt (x, grid) {359 // TODO: Binary search, and be rid of this slow method360 let ret = []361 for (let i = 0; i < grid.length; i++) {362 if (grid[i].x === x) {363 ret.push(grid[i])364 }365 }366 return ret367 }368 solve (numGridPts) {369 let grid = this._createGrid(numGridPts)370 let EI = this.moment * this.modulus371 if (EI <= 0) {372 if (this.moment <= 0) {373 throw new Error('Cannot solve beam: moment <= 0')374 }375 if (this.modulus <= 0) {376 throw new Error('Cannot solve beam: modulus <= 0')377 }378 }379 // Up to this point we have collected downward forces on the beam including point loads and constant or distributed loads. We have ordered those forces and made a grid representing distances and magnitudes380 // Here are the next steps:381 // 1-Determine the reaction forces at A (left side) and B (right side) of the beam. Here is an example beam382 // q/x 2q383 // _______________ |384 // | | | | | | |385 // _v__v__v__v__v__v_____________v______________386 // ^ ^387 // A x---> B388 // We take the sum of the moments about A to find the reaction force at B and then subtract the reaction force B from the sum of all of the forces to determine A389 // Shear has units of force lbf or N390 // 2-Bending moment has units of lbf*ft or N*m is the integral of the shear with respect to X. We calculate it using Simpson's rule391 // Given: Applied weight w(x) of all point loads, pins, fixed supports, and continuous loads392 // Step 1: V(x) = - int{ w(x) dx } + C1393 // Step 2: M(x) = int{ V(x) dx } + C1*x + C2394 // Step 3: th(x) = int{ M(x) / EI dx } + C1*x^2/2 + C2*x + c3395 // Step 4: y(x) = int{ th(x) dx } + C1*x^3/6 + C2*x^2/2 + C3*x + C4396 // Unknowns:397 // Add 4 unknowns from the constants of integration: C1, C2, C3, C4398 // Add 2 unknowns for each fixed end (applied force and applied moment)399 // Add 1 unknown for each pin (applied force)400 // Equations:401 // Add 2 equations for each end of the beam (4 total): V = 0, M = 0402 // Add 2 equations for each end that is fixed: th = 0, y = 0403 // Add 1 equation for every pin: y = 0404 // Because V(0) = 0 and M(0) = 0 for every beam (non-zero boundary conditions are assumed to be unknown applied forces and moments), we can eliminate two unknowns immediately: C1 = 0 and C2 = 0.405 // Examples:406 // Simply supported beam (V is zero in this case because there is a discontinuity at each endpoint, the zero value is at the outside of the discontinuity)407 //408 // |409 // _____V______410 // ^ ^411 // p1 p2412 //413 // Unknowns (4): C3, C4, p1, p2414 // Equations (4): V(L) = 0, M(L) = 0, y(0) = 0, y(L) = 0415 // DOF: 0416 // Fixed-free beam417 //418 // |419 // //|_____V______420 // //|421 // p1, m1422 //423 // Unknowns (4): C3, C4, p1, m1424 // Equations (4): th(0) = 0, y(0) = 0, V(L) = 0, M(L) = 0425 // DOF: 0426 // Fixed-pin beam427 //428 // |429 // //|_____V______430 // //| ^431 // p1, m1 p2432 //433 // Unknowns (5): C3, C4, p1, m1, p2434 // Equations (5): th(0) = 0, y(0) = 0, V(L) = 0, M(L) = 0, y(L) = 0435 // DOF: 0436 // Fixed-fixed beam437 //438 // |439 // //|_____V______|//440 // //| |//441 // p1, m1 p2, m2442 //443 // Unknowns (6): C3, C4, p1, m1, p2, m2444 // Equations (6): th(0) = 0, y(0) = 0, V(L) = 0, M(L) = 0, th(L) = 0, y(L) = 0445 // DOF: 0446 // Three pins in middle of beam447 // ___________448 // ^ ^ ^449 // p1 p2 p3450 //451 // Unknowns (5): C3, C4, p1, p2, p3452 // Equations (5): V(L) = 0, M(L) = 0, y(1) = 0, y(2) = 0, y(3) = 0453 // DOF: 0454 // Unsupported beam455 // ___________456 //457 // Unknowns (2): C3, C4458 // Equations (2): V(L) = 0, M(L) = 0459 // DOF: 0460 // Will result in singular matrix when solving461 // Beam with single pin462 // ___________463 // ^464 // p1465 //466 // Unknowns (3): C3, C4, p1467 // Equations (3): V(L) = 0, M(L) = 0, y(1) = 0468 // DOF: 0469 // Will result in singular matrix when solving470 // Unbalanced beam471 //472 // |473 // ________V__474 // ^ ^475 // p1 p2476 //477 // Unknowns (4): C3, C4, p1, p2478 // Equations (4): V(L) = 0, M(L) = 0, y(1) = 0, y(2) = 0479 // DOF: 0480 // Will solve just fine with one of the pins having a negative load (but it's a pin, not a roller, so it's okay)481 // Do integrations in one pass of the for loop, so that we can use intermediate values without having to store them between loops.482 grid[0].vbar = 0483 grid[0].mbar = 0484 grid[0].thetabar = 0485 grid[0].ybar = 0486 for (let i = 0; i < grid.length - 1; i++) {487 const a = grid[i].x488 const b = grid[i + 1].x489 if (grid[i].isPointLoad && grid[i].relationToFeature === -1) {490 // This is a point load.491 // Add the contributions from the point load to Vbar. The other variables, I think, will remain unchanged.492 grid[i + 1].vbar = grid[i].vbar + grid[i].pointLoad493 grid[i + 1].mbar = grid[i].mbar494 grid[i + 1].thetabar = grid[i].thetabar495 grid[i + 1].ybar = grid[i].ybar496 } else if (a !== b) {497 // Further subdivide this grid point into 4 sections. The final two integrations will halve the number of points, which will bring us back to the original grid.498 // Intermediate points:499 const ab = (a + b) / 2500 const aab = (a + ab) / 2501 const abb = (ab + b) / 2502 // Evaluate w at intemediate points503 const fa = this.contLoad(a) // TODO: Could reuse previous loop's fb504 const faab = this.contLoad(aab)505 const fab = this.contLoad(ab)506 const fabb = this.contLoad(abb)507 const fb = this.contLoad(b)508 // Calculate Vbar using trapezoid rule509 const vbara = grid[i].vbar510 const vbaraab = vbara + (faab + fa) / 2 * (aab - a)511 const vbarab = vbaraab + (fab + faab) / 2 * (ab - aab)512 const vbarabb = vbarab + (fabb + fab) / 2 * (abb - ab)513 const vbarb = vbarabb + (fb + fabb) / 2 * (b - abb)514 // Calculate mbar using trapezoid rule515 const mbara = grid[i].mbar516 const mbaraab = mbara + (vbaraab + vbara) / 2 * (aab - a)517 const mbarab = mbaraab + (vbarab + vbaraab) / 2 * (ab - aab)518 const mbarabb = mbarab + (vbarabb + vbarab) / 2 * (abb - ab)519 const mbarb = mbarabb + (vbarb + vbarabb) / 2 * (b - abb)520 // Calculate thetabar using Simpson's rule521 const thetabara = grid[i].thetabar522 const thetabarab = thetabara + (mbara + 4 * mbaraab + mbarab) / 6 * (ab - a) / EI523 const thetabarb = thetabarab + (mbarab + 4 * mbarabb + mbarb) / 6 * (b - ab) / EI524 // Calculate ybar using Simpson's rule525 const ybara = grid[i].ybar526 const ybarb = ybara + (thetabara + 4 * thetabarab + thetabarb) / 6 * (b - a)527 // Store results in grid528 grid[i + 1].vbar = vbarb529 grid[i + 1].mbar = mbarb530 grid[i + 1].thetabar = thetabarb531 grid[i + 1].ybar = ybarb532 } else if ((grid[i].isPin || grid[i].isFixedAnchor) && grid[i].relationToFeature === -1) {533 // This is not a point load and not a normal grid interval, it must be an anchor. The *bar variables do not include contributions from anchors.534 grid[i + 1].vbar = grid[i].vbar535 grid[i + 1].mbar = grid[i].mbar536 grid[i + 1].thetabar = grid[i].thetabar537 grid[i + 1].ybar = grid[i].ybar538 } else {539 console.log(grid[i])540 throw new Error('Unsupported type of grid point.')541 }542 }543 // The numerical integration is complete. The *bar variables are calculated at each grid point.544 // We need to decide where in the matrix each equation and unknown will live.545 // Generally, we'll go from left to right. This will keep the matrix mostly diagonal.546 // The variables and equations will be indexed in this order:547 // if fixed anchor on left: add two variables m0 and p0; add equations y(0) = 0 and theta(0) = 0548 // for each pin: add variable p_i; add equation y(i) = 0549 // if fixed anchor on right: add variables mL and pL; add equations y(L) = 0 and theta(L) = 0550 // add variables c3 and c4; add equation m(L) = 0, v(L) = 0551 // If fixed anchor on left, first two variables will be m0 and p0.552 // First two equations will be:553 // y(0) = 0, which becomes c_4 = 0554 // theta(0) = 0, which becomes c_3 = 0555 // For each pin: add variable p_i556 // Add equation y(x_i) = 0557 // Which becomes:558 // ybar(x_i) + 1/EI * (-p0 x_i^3/6 - sum(j<i, p_j (x_i - x_j)^3/6) + m0 x_i^2/2) + c_3 x_i + c_4 = 0559 // If fixed anchor on right, next two variables will be mL and pL.560 // Next two equations will be: y(L) = 0, which becomes:561 // ybar(L) + 1/EI (-p0 L^3/6 - sum(j, p_j (L - x_j)^3/6) + m0 L^2/2) + c_3 L + c_4 = 0562 // and theta(L) = 0, which becomes:563 // thetabar(L) + 1/EI (-p0 L^2/2 - sum(j, p_j (L - x_j)^2/2) + m0 L) + c_3 = 0564 // The final two equations are:565 // m(L) = 0, which becomes:566 // mbar(L) - p0 L - sum(j, p_j (L - x_j) + m0 + mL = 0567 // and v(L) = 0, which becomes:568 // vbar(L) - p0 - sum(j, p_i) - pL = 0569 // List of variables:570 // m0 (if left fixed anchor)571 // p0 (if left fixed anchor)572 // p_i (for each pin)573 // mL (if right fixed anchor)574 // pL (if right fixed anchor)575 // c3576 // c4577 // Equations, written again in matrix form with terms in correct order578 // m0 p0 ...p_i mL pL c_3 c_4 constant579 // c_4 = 0 If fixed anchor on left580 // c_3 = 0 If fixed anchor on left581 //582 // For each pin i:583 // m0 x_i^2/2EI - p0 x_i^3/6EI - sum(j < i, p_j (x_i - x_j) ^ 3 / 6EI) + c_3 x_i + c_4 = -ybar(x_i)584 // Spelling it out:585 // For pin p_1 located at x_1, y(x_1) = 0, or:586 // m0 x_1^2/2EI - p0 x_1^3/6EI + c_3 x_1 + c_4 = -ybar(x_1)587 // For pin p_2 located at x_2, y(x_2) = 0, or:588 // m0 x_2^2/2EI - p0 x_2^3/6EI - p_1 (x_2 - x_1) ^ 3 / 6EI + c_3 x_2 + c_4 = -ybar(x_2)589 // For pin p_3 located at x_3, y(x_3) = 0, or:590 // m0 x_3^2/2EI - p0 x_3^3/6EI - p_1 (x_3 - x_1) ^ 3 / 6EI - p_2 (x_3 - x_2) ^ 3 / 6EI + c_3 x_3 + c_4 = -ybar(x_3)591 // And so on. Written in matrix standard form, these become:592 // For pin 1:593 // m0 [x_1^2/2EI] p0 [-x_1^3/6EI] p_1 [0] p_2 [0] p_3[0] mL [0] pL [0] c_3 [x_1] c_4 [1] = -ybar(x_1)594 // For pin 2:595 // m0 [x_2^2/2EI] p0 [-x_2^3/6EI] p_1 [-(x_2-x_1)^3/6EI] p_2 [0] p_3[0] mL [0] pL [0] c_3 [x_2] c_4 [1] = -ybar(x_2)596 // For pin 3:597 // m0 [x_3^2/2EI] p0 [-x_3^3/6EI] p_1 [-(x_3-x_1)^3/6EI] p_2 [-(x_3-x_2)^3/6EI] p_3[0] mL [0] pL [0] c_3 [x_3] c_4 [1] = -ybar(x_3)598 // m(L) = 0:599 // m0 [1] p0 [-L] p_1 [-(L-x_1)] p_2 [-(L-x_2)] p_3 [-(L-x_3)] mL [1] pL [0] c_3 [0] c_4 [0] = -mbar(L)600 // v(L) = 0:601 // m0 [0] p0 [-1] p_1 [-1] p_2 [-1] p_3 [-1] mL [0] pL [-1] c_3 [0] c_4 [0] = -vbar(L)602 // Not as hard as I thought! Bookkeeping will be straightforward. Will have to make sure we take the values from the correct sides of the discontinuities, it that is a concern.603 // Matrix is predominantly lower diagonal, as a result of arranging things from left to right. You could almost solve it by straight Gauss-Jordan elimination.604 let A = []605 let b = []606 // List of variables:607 // m0 (if left fixed anchor) 0608 // p0 (if left fixed anchor) 1609 // p_i (for each pin) nLeftDofs + i (starting at i=0)610 // mL (if right fixed anchor) nLeftDofs + nPinDofs611 // pL (if right fixed anchor) nLeftDofs + nPinDofs + 1612 // c3 nLeftDofs + nPinDofs + nRightDofs613 // c4 nLeftDofs + nPinDofs + nRightDofs + 1614 let nLeftDofs = this.anchorLeft === 'fixed' ? 2 : 0615 let nRightDofs = this.anchorRight === 'fixed' ? 2 : 0616 let nPinDofs = this.pins.length617 let nDofs = nLeftDofs + nPinDofs + nRightDofs + 2618 // Column indices of each variable619 let m0Idx, p0Idx, pIdx, mLIdx, pLIdx, c3Idx, c4Idx620 // Row indices of each equation621 let eqY0Idx, eqTheta0Idx, eqYxIdx, eqYLIdx, eqThetaLIdx, eqMLIdx, eqVLIdx622 // Set column and row indices (by adjusting the indices here, we can rearrange the matrix without breaking anything else)623 if (this.anchorLeft === 'fixed') {624 m0Idx = 0625 p0Idx = 1626 eqY0Idx = 0627 eqTheta0Idx = 1628 }629 pIdx = nLeftDofs // + i, with i starting at 0 for the first pin630 eqYxIdx = nLeftDofs // + i, with i starting at 0 for the first pin631 if (this.anchorRight === 'fixed') {632 mLIdx = nLeftDofs + nPinDofs633 pLIdx = nLeftDofs + nPinDofs + 1634 eqYLIdx = nLeftDofs + nPinDofs635 eqThetaLIdx = nLeftDofs + nPinDofs + 1636 }637 c3Idx = nLeftDofs + nPinDofs + nRightDofs638 c4Idx = nLeftDofs + nPinDofs + nRightDofs + 1639 eqMLIdx = nLeftDofs + nPinDofs + nRightDofs640 eqVLIdx = nLeftDofs + nPinDofs + nRightDofs + 1641 // Initialize A and b to 0's642 for (let i = 0; i < nDofs; i++) {643 A[i] = []644 for (let j = 0; j < nDofs; j++) {645 A[i][j] = 0646 }647 }648 // Set coefficients of A and b649 if (this.anchorLeft === 'fixed') {650 // y(0) = 0, or c_4 = 0651 A[eqY0Idx][c4Idx] = 1652 b[eqY0Idx] = 0653 // theta(0) = 0, or c_3 = 0654 A[eqTheta0Idx][c3Idx] = 1655 b[eqTheta0Idx] = 0656 }657 for (let i = 0; i < this.pins.length; i++) {658 // y(x_i) = 0, or:659 // ybar(x_i) + 1/EI * (-p0 x_i^3/6 - sum(j<i, p_j (x_i - x_j)^3/6) + m0 x_i^2/2) + c_3 x_i + c_4 = 0660 // For the third pin, for example:661 // m0 [x_3^2/2EI] p0 [-x_3^3/6EI] p_1 [-(x_3-x_1)^3/6EI] p_2 [-(x_3-x_2)^3/6EI] p_3[0] mL [0] pL [0] c_3 [x_3] c_4 [1] = -ybar(x_3)662 let xI = this.pins[i].x663 A[eqYxIdx + i][m0Idx] = xI ** 2 / (2 * EI)664 A[eqYxIdx + i][p0Idx] = -(xI ** 3) / (6 * EI)665 for (let j = 0; j < i; j++) {666 let xJ = this.pins[j].x667 A[eqYxIdx + i][pIdx + j] = -((xI - xJ) ** 3) / (6 * EI)668 }669 A[eqYxIdx + i][c3Idx] = xI670 A[eqYxIdx + i][c4Idx] = 1671 b[eqYxIdx + i] = -this._findGridPt(xI, grid)[0].ybar // ybar will be equal on both sides of the discontinuity, so both grid points will match672 }673 let L = this.length674 if (this.anchorRight === 'fixed') {675 // y(L) = 0676 // ybar(L) + 1/EI (-p0 L^3/6 - sum(j, p_j (L - x_j)^3/6) + m0 L^2/2) + c_3 L + c_4 = 0677 // -p0 L^3/(6 EI) + sum(j, -p_j (L - x_j)^3/(6EI)) + m0 L^2/(2EI) + c_3 L + c_4 = -ybar(L)678 A[eqYLIdx][p0Idx] = -(L ** 3) / (6 * EI)679 for (let j = 0; j < this.pins.length; j++) {680 let xJ = this.pins[j].x681 A[eqYLIdx][pIdx + j] = -((L - xJ) ** 3) / (6 * EI)682 }683 A[eqYLIdx][m0Idx] = L ** 2 / (2 * EI)684 A[eqYLIdx][c3Idx] = L685 A[eqYLIdx][c4Idx] = 1686 b[eqYLIdx] = -grid[grid.length - 1].ybar // Final grid point687 // theta(L) = 0688 // -p0 L^2/(2EI) + sum(j, -p_j (L - x_j)^2/(2EI)) + m0 L / EI + c_3 = -thetabar(L)689 A[eqThetaLIdx][p0Idx] = -(L ** 2) / (2 * EI)690 for (let j = 0; j < this.pins.length; j++) {691 let xJ = this.pins[j].x692 A[eqThetaLIdx][pIdx + j] = -((L - xJ) ** 2) / (2 * EI)693 }694 A[eqThetaLIdx][m0Idx] = L / EI695 A[eqThetaLIdx][c3Idx] = 1696 b[eqThetaLIdx] = -grid[grid.length - 1].thetabar697 }698 // m(L) = 0699 // m0 [1] p0 [-L] p_1 [-(L-x_1)] p_2 [-(L-x_2)] p_3 [-(L-x_3)] mL [1] pL [0] c_3 [0] c_4 [0] = -mbar(L)700 A[eqMLIdx][m0Idx] = 1701 A[eqMLIdx][p0Idx] = -L702 for (let j = 0; j < this.pins.length; j++) {703 let xJ = this.pins[j].x704 A[eqMLIdx][pIdx + j] = -(L - xJ)705 }706 A[eqMLIdx][mLIdx] = 1707 b[eqMLIdx] = -grid[grid.length - 1].mbar // Final grid point, right side of discontinuity, if there is one (which I don't think can happen with mbar)708 // v(L) = 0:709 // m0 [0] p0 [-1] p_1 [-1] p_2 [-1] p_3 [-1] mL [0] pL [-1] c_3 [0] c_4 [0] = -vbar(L)710 A[eqVLIdx][p0Idx] = -1711 for (let j = 0; j < this.pins.length; j++) {712 A[eqVLIdx][pIdx + j] = -1713 }714 A[eqVLIdx][pLIdx] = -1715 b[eqVLIdx] = -grid[grid.length - 1].vbar // Final grid point. I think there could be a discontinuity here if there is a point load on the very end of the beam.716 let soln717 let error718 try {719 soln = math.lusolve(A, b)720 } catch (ex) {721 error = 'singular_matrix'722 }723 if (!error) {724 // Assign result to named variables for convenience725 if (this.anchorLeft === 'fixed') {726 soln.m0 = soln[m0Idx][0]727 soln.p0 = soln[p0Idx][0]728 }729 for (let i = 0; i < this.pins.length; i++) {730 soln[`pin${i}`] = soln[pIdx + i][0]731 }732 if (this.anchorRight === 'fixed') {733 soln.mL = soln[mLIdx][0]734 soln.pL = soln[pLIdx][0]735 }736 soln.c3 = soln[c3Idx][0]737 soln.c4 = soln[c4Idx][0]738 // Calculate actual values of v, m, theta, and y at the grid points by adding the forces and moments from pins and anchors to the *bar variables739 let pinGridIdxs = [] // Indices of grid points containing pins, encountered so far740 for (let i = 0; i < grid.length; i++) {741 if (grid[i].isPin && grid[i].relationToFeature === 1) {742 pinGridIdxs.push(i)743 }744 let x = grid[i].x745 grid[i].v = grid[i].vbar746 grid[i].m = grid[i].mbar747 grid[i].theta = grid[i].thetabar748 grid[i].y = grid[i].ybar749 grid[i].theta += soln.c3750 grid[i].y += soln.c3 * grid[i].x + soln.c4751 if (this.anchorLeft === 'fixed' && i > 0) {752 // Apply left fixed anchor753 grid[i].v += -soln.p0754 grid[i].m += -soln.p0 * x + soln.m0755 grid[i].theta += (-0.5 * soln.p0 * x ** 2 + soln.m0 * x) / EI756 grid[i].y += (-soln.p0 * x ** 3 / 6 + soln.m0 * 0.5 * x ** 2) / EI757 }758 if (grid[i].isFixedAnchor && grid[i].relationToFeature === 1 && this.anchorRight === 'fixed' && i === grid.length - 1 && x === this.length) {759 // Apply right fixed anchor760 grid[i].v += -soln.pL761 grid[i].m += soln.mL762 }763 for (let k = 0; k < pinGridIdxs.length; k++) {764 let j = pinGridIdxs[k]765 let pj = soln[`pin${k}`]766 let pinx = grid[j].x767 grid[i].v -= pj768 grid[i].m -= pj * (x - pinx)769 grid[i].theta -= pj * 0.5 * (x - pinx) ** 2 / EI770 grid[i].y -= pj * Math.pow(x - pinx, 3) / EI / 6771 }772 }773 this.soln = soln774 }775 this.grid = grid776 this.error = error777 this._isSolved = true778 // TODO: what exactly solve will return, and how exactly this Beam will be changed when it has solved779 // For now, just return the grid, A, and b, which we can use to check to see if everything's working right780 }...
Beam.test.js
Source:Beam.test.js
1import assert from 'assert'2import approx from './approx'3import Beam from '../src/Beam.js'4describe('beam', () => {5 describe('constructor', () => {6 it('should construct an empty beam', () => {7 const b = new Beam()8 assert.strictEqual(b._length, null)9 assert.strictEqual(b._moment, null)10 assert.strictEqual(b._modulus, null)11 assert.deepStrictEqual(b._pointLoads, [])12 assert.strictEqual(b.contLoad(3.14), 0)13 assert.deepStrictEqual(b._anchor, ['free', 'free'])14 assert.strictEqual(b._isSolved, false)15 })16 it('should create a proxy for _pointLoads', () => {17 const b = new Beam()18 assert(b._pointLoadsProxy instanceof Array) // Not instanceof Proxy, as that should be totally transparent19 })20 it('should throw if called without "new" keyword', () => {21 assert.throws(() => Beam(), /Class constructor Beam cannot be invoked without 'new'/)22 })23 })24 describe('length', () => {25 it('should get and set', () => {26 const b = new Beam()27 b.length = 528 assert.strictEqual(b.length, 5)29 })30 it('should throw if given the wrong type or a negative number', () => {31 const b = new Beam()32 assert.throws(() => { b.length = 'five' }, /TypeError: length must be a positive number/)33 assert.throws(() => { b.length = -5 }, /TypeError: length must be a positive number/)34 })35 it('should reset _isSolved flag', () => {36 const b = new Beam()37 b._isSolved = true38 b.length = 539 assert.strictEqual(b._isSolved, false)40 })41 })42 describe('moment', () => {43 it('should get and set', () => {44 const b = new Beam()45 b.moment = 546 assert.strictEqual(b.moment, 5)47 })48 it.skip('should support functions', () => {49 const b = new Beam()50 b.moment = x => x * x51 assert.strictEqual(b.moment(10), 100)52 })53 it('should throw if given the wrong type', () => {54 const b = new Beam()55 assert.throws(() => { b.moment = 'five' }, /TypeError: moment must be/)56 assert.throws(() => { b.moment = -5 }, /TypeError: moment must be/)57 })58 it('should reset _isSolved flag', () => {59 const b = new Beam()60 b._isSolved = true61 b.moment = 562 assert.strictEqual(b._isSolved, false)63 })64 })65 describe('modulus', () => {66 it('should get and set', () => {67 const b = new Beam()68 b.modulus = 4069 assert.strictEqual(b.modulus, 40)70 })71 it('should throw if given the wrong type', () => {72 const b = new Beam()73 assert.throws(() => { b.modulus = 'forty' }, /TypeError: modulus must be/)74 assert.throws(() => { b.modulus = -40 }, /TypeError: modulus must be/)75 })76 it('should reset _isSolved flag', () => {77 const b = new Beam()78 b._isSolved = true79 b.modulus = 4080 assert.strictEqual(b._isSolved, false)81 })82 })83 describe('contLoad', () => {84 it('should get and set', () => {85 const b = new Beam()86 b.contLoad = x => x * x87 assert.strictEqual(b.contLoad(10), 100)88 })89 it('should throw if given the wrong type', () => {90 const b = new Beam()91 assert.throws(() => { b.contLoad = 'five' }, /TypeError: contLoad must be/)92 assert.throws(() => { b.contLoad = 5 }, /TypeError: contLoad must be/)93 })94 it('should reset _isSolved flag', () => {95 const b = new Beam()96 b._isSolved = true97 b.contLoad = x => x * x98 assert.strictEqual(b._isSolved, false)99 })100 })101 describe('anchorLeft, anchorRight', () => {102 it('should get and set', () => {103 const b = new Beam()104 b.anchorLeft = 'fixed'105 b.anchorRight = 'free'106 assert.strictEqual(b.anchorLeft, 'fixed')107 assert.strictEqual(b.anchorRight, 'free')108 assert.strictEqual(b._anchor[0], 'fixed')109 assert.strictEqual(b._anchor[1], 'free')110 })111 it('should throw if given the wrong type', () => {112 const b = new Beam()113 assert.throws(() => { b.anchorLeft = 'floating' }, /TypeError: anchorLeft must be/)114 assert.throws(() => { b.anchorRight = 5 }, /TypeError: anchorRight must be/)115 })116 it('should reset _isSolved flag', () => {117 const b = new Beam()118 b._isSolved = true119 b.anchorLeft = 'free'120 assert.strictEqual(b._isSolved, false)121 b._isSolved = true122 b.anchorRight = 'fixed'123 assert.strictEqual(b._isSolved, false)124 })125 })126 describe('pointLoads', () => {127 it('should get and set', () => {128 const b = new Beam()129 b.pointLoads = [{ x: 4, w: 5 }, { x: 6, w: 3 }]130 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }, { x: 6, w: 3 }])131 })132 it('should throw if given the wrong type', () => {133 const b = new Beam()134 assert.throws(() => { b.pointLoads = { x: 4, w: 5 } }, /TypeError: pointLoads must be an array/)135 })136 it('assignment should reset _isSolved flag', () => {137 const b = new Beam()138 b._isSolved = true139 b.pointLoads = [{ x: 4, w: 5 }, { x: 6, w: 3 }]140 assert.strictEqual(b._isSolved, false)141 })142 it('property assignment should reset _isSolved flag', () => {143 const b = new Beam()144 b._isSolved = true145 b.pointLoads[0] = { x: 4, w: 5 }146 b.pointLoads[1] = { x: 6, w: 3 }147 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }, { x: 6, w: 3 }])148 assert.strictEqual(b._isSolved, false)149 })150 it('array prototype method calls should reset _isSolved flag', () => {151 const b = new Beam()152 b._isSolved = true153 b.pointLoads.push({ x: 4, w: 5 })154 b.pointLoads.push({ x: 6, w: 3 })155 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }, { x: 6, w: 3 }])156 assert.strictEqual(b._isSolved, false)157 b._isSolved = true158 assert.strictEqual(b._isSolved, true)159 b.pointLoads.pop()160 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }])161 assert.strictEqual(b._isSolved, false)162 })163 it('should throw if attempting to add an invalid pointLoad', () => {164 let b = new Beam()165 assert.throws(() => { b.pointLoads.push({ x: 'not a valid point load', w: 'definitely not' }) }, /A point load must be an object of type: \{ x: number, w: number \}/)166 b = new Beam()167 assert.throws(() => { b.pointLoads[1] = { x: 4 } }, /A point load must be an object of type: \{ x: number, w: number \}/)168 b = new Beam()169 let a170 assert.throws(() => { b.pointLoads.push(a) }, /A point load must be an object of type: \{ x: number, w: number \}/)171 })172 it('should throw if attempting to mutate a pointLoad', () => {173 const b = new Beam()174 let a = { x: 4, w: 5 }175 b.pointLoads.push(a)176 assert.throws(() => { a.x = 6 }, /Cannot assign to read only property/)177 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }])178 a = b.addPointLoad({ x: 6, w: 8 })179 assert.throws(() => { a.x = 10 }, /Cannot assign to read only property/)180 assert.deepStrictEqual(b.pointLoads, [{ x: 4, w: 5 }, { x: 6, w: 8 }])181 const b2 = new Beam()182 a = { x: 20, w: 30 }183 b2.addPointLoad(a)184 assert.throws(() => { a.x = 25 }, /Cannot assign to read only property/)185 })186 })187 describe('addPointLoad', () => {188 it('should add a point load', () => {189 const b = new Beam()190 let a = b.addPointLoad(10, 20)191 assert.deepStrictEqual(b.pointLoads, [{ x: 10, w: 20 }])192 assert.deepStrictEqual(a, { x: 10, w: 20 })193 a = b.addPointLoad({ x: 40, w: 50 })194 assert.deepStrictEqual(b.pointLoads, [{ x: 10, w: 20 }, { x: 40, w: 50 }])195 assert.deepStrictEqual(a, { x: 40, w: 50 })196 })197 it('should reset _isSolved flag', () => {198 const b = new Beam()199 b._isSolved = true200 b.addPointLoad(10, 20)201 assert.strictEqual(b._isSolved, false)202 })203 })204 describe('removePointLoad', () => {205 it('should remove a point load', () => {206 const b = new Beam()207 const p1 = b.addPointLoad(10, 20)208 b.addPointLoad(15, 30)209 assert.deepStrictEqual(b.pointLoads, [{ x: 10, w: 20 }, { x: 15, w: 30 }])210 b.removePointLoad(p1)211 assert.deepStrictEqual(b.pointLoads, [{ x: 15, w: 30 }])212 })213 it('should reset _isSolved flag', () => {214 const b = new Beam()215 const p1 = b.addPointLoad(10, 20)216 b._isSolved = true217 assert.strictEqual(b._isSolved, true)218 b.removePointLoad(p1)219 assert.strictEqual(b._isSolved, false)220 })221 it('should throw if the point load is not found', () => {222 const b = new Beam()223 const p1 = b.addPointLoad(10, 20)224 b.addPointLoad(15, 30)225 assert.deepStrictEqual(b.pointLoads, [{ x: 10, w: 20 }, { x: 15, w: 30 }])226 assert.throws(() => { b.removePointLoad({ x: 10, w: 20 }) }, /Error: The given point load was not found. \(Point loads are matched by reference, not value.\)/)227 assert.doesNotThrow(() => { b.removePointLoad(p1) })228 assert.deepStrictEqual(b.pointLoads, [{ x: 15, w: 30 }])229 assert.throws(() => { b.removePointLoad(p1) }, /Error: The given point load was not found. \(Point loads are matched by reference, not value.\)/)230 })231 })232 describe('pins', () => {233 it('should get and set', () => {234 const b = new Beam()235 b.pins = [{ x: 4 }, { x: 6 }]236 assert.deepStrictEqual(b.pins, [{ x: 4 }, { x: 6 }])237 })238 it('should throw if given the wrong type', () => {239 const b = new Beam()240 assert.throws(() => { b.pins = { x: 4 } }, /TypeError: pins must be an array/)241 })242 it('assignment should reset _isSolved flag', () => {243 const b = new Beam()244 b._isSolved = true245 b.pins = [{ x: 4 }, { x: 6 }]246 assert.strictEqual(b._isSolved, false)247 })248 it('property assignment should reset _isSolved flag', () => {249 const b = new Beam()250 b._isSolved = true251 b.pins[0] = { x: 4 }252 b.pins[1] = { x: 6 }253 assert.deepStrictEqual(b.pins, [{ x: 4 }, { x: 6 }])254 assert.strictEqual(b._isSolved, false)255 })256 it('array prototype method calls should reset _isSolved flag', () => {257 const b = new Beam()258 b._isSolved = true259 b.pins.push({ x: 4 })260 b.pins.push({ x: 6 })261 assert.deepStrictEqual(b.pins, [{ x: 4 }, { x: 6 }])262 assert.strictEqual(b._isSolved, false)263 b._isSolved = true264 assert.strictEqual(b._isSolved, true)265 b.pins.pop()266 assert.deepStrictEqual(b.pins, [{ x: 4 }])267 assert.strictEqual(b._isSolved, false)268 })269 it('should throw if attempting to add an invalid pin', () => {270 let b = new Beam()271 assert.throws(() => { b.pins.push({ x: 'not a valid pin' }) }, /A pin must be an object with a single property `x` of type number/)272 b = new Beam()273 assert.throws(() => { b.pins[1] = { y: 4 } }, /A pin must be an object with a single property `x` of type number/)274 b = new Beam()275 let a276 assert.throws(() => { b.pins.push(a) }, /A pin must be an object with a single property `x` of type number/)277 })278 it('should throw if attempting to mutate a pin', () => {279 const b = new Beam()280 let a = { x: 4 }281 b.pins.push(a)282 assert.throws(() => { a.x = 6 }, /Cannot assign to read only property/)283 assert.deepStrictEqual(b.pins, [{ x: 4 }])284 a = b.addPin({ x: 6 })285 assert.throws(() => { a.x = 10 }, /Cannot assign to read only property/)286 assert.deepStrictEqual(b.pins, [{ x: 4 }, { x: 6 }])287 const b2 = new Beam()288 a = { x: 20 }289 b2.addPin(a)290 assert.throws(() => { a.x = 25 }, /Cannot assign to read only property/)291 })292 })293 describe('addPin', () => {294 it('should add a pin', () => {295 const b = new Beam()296 let a = b.addPin(10)297 assert.deepStrictEqual(b.pins, [{ x: 10 }])298 assert.deepStrictEqual(a, { x: 10 })299 a = b.addPin({ x: 40 })300 assert.deepStrictEqual(b.pins, [{ x: 10 }, { x: 40 }])301 assert.deepStrictEqual(a, { x: 40 })302 })303 it('should reset _isSolved flag', () => {304 const b = new Beam()305 b._isSolved = true306 b.addPin(10)307 assert.strictEqual(b._isSolved, false)308 })309 })310 describe('removePin', () => {311 it('should remove a pin', () => {312 const b = new Beam()313 const p1 = b.addPin(10)314 b.addPin(15)315 assert.deepStrictEqual(b.pins, [{ x: 10 }, { x: 15 }])316 b.removePin(p1)317 assert.deepStrictEqual(b.pins, [{ x: 15 }])318 })319 it('should reset _isSolved flag', () => {320 const b = new Beam()321 const p1 = b.addPin(10)322 b._isSolved = true323 assert.strictEqual(b._isSolved, true)324 b.removePin(p1)325 assert.strictEqual(b._isSolved, false)326 })327 it('should throw if the pin is not found', () => {328 const b = new Beam()329 const p1 = b.addPin(10)330 b.addPin(15)331 assert.deepStrictEqual(b.pins, [{ x: 10 }, { x: 15 }])332 assert.throws(() => { b.removePin({ x: 10 }) }, /Error: The given pin was not found. \(Pins are matched by reference, not value.\)/)333 assert.doesNotThrow(() => { b.removePin(p1) })334 assert.deepStrictEqual(b.pins, [{ x: 15 }])335 assert.throws(() => { b.removePin(p1) }, /Error: The given pin was not found. \(Pins are matched by reference, not value.\)/)336 })337 })338 describe('_createGrid', () => {339 it('should create an evenly spaced grid', () => {340 const b = new Beam()341 b.length = 10342 let grid = b._createGrid(5)343 assert.deepStrictEqual(grid, [{ x: 0 }, { x: 2 }, { x: 4 }, { x: 6 }, { x: 8 }, { x: 10 }])344 grid = b._createGrid(1)345 assert.deepStrictEqual(grid, [{ x: 0 }, { x: 10 }])346 })347 it('should add point loads to the grid and sort them', () => {348 const b = new Beam()349 b.length = 10350 b.addPointLoad(5, 10)351 b.addPointLoad(8.5, 10)352 let grid = b._createGrid(5)353 assert.deepStrictEqual(grid, [354 { x: 0 },355 { x: 2 },356 { x: 4 },357 {358 x: 5,359 isPointLoad: true,360 pointLoad: 10,361 relationToFeature: -1362 },363 {364 x: 5,365 isPointLoad: true,366 pointLoad: 10,367 relationToFeature: 1368 },369 { x: 6 },370 { x: 8 },371 {372 x: 8.5,373 isPointLoad: true,374 pointLoad: 10,375 relationToFeature: -1376 },377 {378 x: 8.5,379 isPointLoad: true,380 pointLoad: 10,381 relationToFeature: 1382 },383 { x: 10 }384 ])385 })386 it('should replace an existing grid point if a point load falls on the grid', () => {387 const b = new Beam()388 b.length = 10389 b.addPointLoad(4, 10)390 b.addPointLoad(6, 10)391 let grid = b._createGrid(5)392 assert.deepStrictEqual(grid, [393 { x: 0 },394 { x: 2 },395 {396 x: 4,397 isPointLoad: true,398 pointLoad: 10,399 relationToFeature: -1400 },401 {402 x: 4,403 isPointLoad: true,404 pointLoad: 10,405 relationToFeature: 1406 },407 {408 x: 6,409 isPointLoad: true,410 pointLoad: 10,411 relationToFeature: -1412 },413 {414 x: 6,415 isPointLoad: true,416 pointLoad: 10,417 relationToFeature: 1418 },419 { x: 8 },420 { x: 10 }421 ])422 })423 it('should combine duplicate point loads', () => {424 const b = new Beam()425 b.length = 10426 b.addPointLoad(4, 10)427 b.addPointLoad(4, 10)428 b.addPointLoad(4, 10)429 let grid = b._createGrid(5)430 assert.deepStrictEqual(grid, [431 { x: 0 },432 { x: 2 },433 {434 x: 4,435 isPointLoad: true,436 pointLoad: 30,437 relationToFeature: -1438 },439 {440 x: 4,441 isPointLoad: true,442 pointLoad: 30,443 relationToFeature: 1444 },445 { x: 6 },446 { x: 8 },447 { x: 10 }448 ])449 })450 it('should add pins to the grid and sort them', () => {451 const b = new Beam()452 b.length = 10453 b.addPin(5)454 b.addPin(8.5)455 let grid = b._createGrid(5)456 assert.deepStrictEqual(grid, [457 { x: 0 },458 { x: 2 },459 { x: 4 },460 {461 x: 5,462 isPin: true,463 relationToFeature: -1464 },465 {466 x: 5,467 isPin: true,468 relationToFeature: 1469 },470 { x: 6 },471 { x: 8 },472 {473 x: 8.5,474 isPin: true,475 relationToFeature: -1476 },477 {478 x: 8.5,479 isPin: true,480 relationToFeature: 1481 },482 { x: 10 }483 ])484 })485 it('should replace an existing grid point if a pin falls on the grid', () => {486 const b = new Beam()487 b.length = 10488 b.addPin(4)489 b.addPin(6)490 let grid = b._createGrid(5)491 assert.deepStrictEqual(grid, [492 { x: 0 },493 { x: 2 },494 {495 x: 4,496 isPin: true,497 relationToFeature: -1498 },499 {500 x: 4,501 isPin: true,502 relationToFeature: 1503 },504 {505 x: 6,506 isPin: true,507 relationToFeature: -1508 },509 {510 x: 6,511 isPin: true,512 relationToFeature: 1513 },514 { x: 8 },515 { x: 10 }516 ])517 })518 it('should allow pins and pointLoads at the same location', () => {519 const b = new Beam()520 b.length = 10521 b.addPointLoad(4, 10)522 b.addPointLoad(5, 20)523 b.addPin(4)524 b.addPin(5)525 let grid = b._createGrid(5)526 assert.deepStrictEqual(grid, [527 { x: 0 },528 { x: 2 },529 {530 x: 4,531 pointLoad: 10,532 isPointLoad: true,533 isPin: true,534 relationToFeature: -1535 },536 {537 x: 4,538 pointLoad: 10,539 isPointLoad: true,540 isPin: true,541 relationToFeature: 1542 },543 {544 x: 5,545 pointLoad: 20,546 isPointLoad: true,547 isPin: true,548 relationToFeature: -1549 },550 {551 x: 5,552 pointLoad: 20,553 isPointLoad: true,554 isPin: true,555 relationToFeature: 1556 },557 { x: 6 },558 { x: 8 },559 { x: 10 }560 ])561 })562 it('should add second grid point for fixed anchors', () => {563 const b = new Beam()564 b.length = 10565 b.anchorRight = 'fixed'566 b.anchorLeft = 'fixed'567 let grid = b._createGrid(5)568 assert.deepStrictEqual(grid, [569 {570 x: 0,571 isFixedAnchor: true,572 relationToFeature: -1573 },574 {575 x: 0,576 isFixedAnchor: true,577 relationToFeature: 1578 },579 { x: 2 },580 { x: 4 },581 { x: 6 },582 { x: 8 },583 {584 x: 10,585 isFixedAnchor: true,586 relationToFeature: -1587 },588 {589 x: 10,590 isFixedAnchor: true,591 relationToFeature: 1592 }593 ])594 })595 it('should throw if numGridPts is not a positive integer', () => {596 const b = new Beam()597 b.length = 10598 assert.throws(() => { b._createGrid() }, /TypeError: numGridPts must be a positive integer./)599 assert.throws(() => { b._createGrid('five') }, /TypeError: numGridPts must be a positive integer./)600 assert.throws(() => { b._createGrid(-3) }, /TypeError: numGridPts must be a positive integer./)601 assert.throws(() => { b._createGrid(10.2) }, /TypeError: numGridPts must be a positive integer./)602 })603 })604 describe('solve', () => {605 it('should calculate vbar, mbar, thetabar, and ybar', () => {606 let b = new Beam()607 b.length = 10608 b.moment = 1609 b.modulus = 1610 b.solve(5)611 assert.deepStrictEqual(b.grid.map(g => g.x), [0, 2, 4, 6, 8, 10])612 assert.deepStrictEqual(b.grid.map(g => g.vbar), [0, 0, 0, 0, 0, 0])613 assert.deepStrictEqual(b.grid.map(g => g.mbar), [0, 0, 0, 0, 0, 0])614 assert.deepStrictEqual(b.grid.map(g => g.thetabar), [0, 0, 0, 0, 0, 0])615 assert.deepStrictEqual(b.grid.map(g => g.ybar), [0, 0, 0, 0, 0, 0])616 // thetabar617 // ybar618 b = new Beam()619 b.length = 10620 b.moment = 1621 b.modulus = 1622 b.contLoad = () => 3623 b.solve(5)624 assert.deepStrictEqual(b.grid.map(g => g.x), [0, 2, 4, 6, 8, 10])625 assert.deepStrictEqual(b.grid.map(g => g.vbar), [0, 6, 12, 18, 24, 30]) // 3 * x626 assert.deepStrictEqual(b.grid.map(g => g.mbar), [0, 6, 24, 54, 96, 150]) // 3 * x^2/2627 assert.deepStrictEqual(b.grid.map(g => g.thetabar), [0, 4, 32, 108, 256, 500]) // 3 * x^3/6628 assert.deepStrictEqual(b.grid.map(g => g.ybar), [0, 2, 32, 162, 512, 1250]) // 3 * x^4/24629 // This will not be an exact answer630 b = new Beam()631 b.length = 10632 b.moment = 1633 b.modulus = 1634 b.contLoad = x => x635 b.solve(5)636 assert.deepStrictEqual(b.grid.map(g => g.x), [0, 2, 4, 6, 8, 10])637 assert.deepStrictEqual(b.grid.map(g => g.vbar), [0, 2, 8, 18, 32, 50]) // x^2/2638 assert.deepStrictEqual(b.grid.map(g => g.mbar), [0, 1.375, 10.75, 36.125, 85.5, 166.875]) // x^3/6. Exact should be [0, 1.333, 10.666, 36, 85.333, 166.666]639 approx.deepEqual(b.grid.map(g => g.thetabar), [0, 0.7083, 10.8333, 54.375, 171.3333, 417.7083]) // x^4/24. Exact should be [0, 0.666, 10.666, 54, 170.666, 416.666]640 approx.deepEqual(b.grid.map(g => g.ybar), [0, 0.30555, 8.7777, 65.5833, 274.888, 836.861]) // x^5/120. Exact should be [0, 0.2666, 8.5333, 64.8, 273.0667, 833.33]641 b = new Beam()642 b.length = 10643 b.moment = 1644 b.modulus = 1645 b.contLoad = x => x646 b.solve(500)647 approx.equal(b.grid[500].x, 10)648 approx.equal(b.grid[500].vbar, 50)649 approx.equal(b.grid[500].mbar, 1000 / 6) // 166.666666...650 approx.equal(b.grid[500].thetabar, 10000 / 24)651 approx.equal(b.grid[500].ybar, 100000 / 120)652 b = new Beam()653 b.length = 100654 b.moment = 1655 b.modulus = 1656 b.contLoad = x => x657 b.solve(500)658 approx.equal(b.grid[500].x, 100)659 approx.equal(b.grid[500].vbar, 5000)660 approx.equal(b.grid[500].mbar, 1000000 / 6)661 approx.equal(b.grid[500].thetabar, 100000000 / 24)662 approx.equal(b.grid[500].ybar, 10000000000 / 120)663 b = new Beam()664 b.length = 10665 b.moment = 1666 b.modulus = 1667 b.addPointLoad(5, 100)668 b.solve(5)669 assert.deepStrictEqual(b.grid.map(g => g.x), [0, 2, 4, 5, 5, 6, 8, 10])670 assert.deepStrictEqual(b.grid.map(g => g.vbar), [0, 0, 0, 0, 100, 100, 100, 100])671 assert.deepStrictEqual(b.grid.map(g => g.mbar), [0, 0, 0, 0, 0, 100, 300, 500])672 assert.deepStrictEqual(b.grid.map(g => g.thetabar), [0, 0, 0, 0, 0, 50, 450, 1250])673 approx.deepEqual(b.grid.map(g => g.ybar), [0, 0, 0, 0, 0, 16.6667, 450, 2083.3333])674 b = new Beam()675 b.length = 10676 b.moment = 1677 b.modulus = 1678 b.addPointLoad(4, 100)679 b.solve(5)680 assert.deepStrictEqual(b.grid.map(g => g.x), [0, 2, 4, 4, 6, 8, 10])681 assert.deepStrictEqual(b.grid.map(g => g.vbar), [0, 0, 0, 100, 100, 100, 100])682 assert.deepStrictEqual(b.grid.map(g => g.mbar), [0, 0, 0, 0, 200, 400, 600])683 assert.deepStrictEqual(b.grid.map(g => g.thetabar), [0, 0, 0, 0, 200, 800, 1800])684 approx.deepEqual(b.grid.map(g => g.ybar), [0, 0, 0, 0, 133.3333, 1066.6667, 3600])685 })686 it('should correctly calculate reactive loads and moments with single fixed anchor', () => {687 // One point load in center688 let b = new Beam()689 b.length = 10690 b.moment = 10691 b.modulus = 10692 b.anchorLeft = 'fixed'693 b.anchorRight = 'free'694 let ptLd = b.addPointLoad(5, 100)695 b.solve(50)696 approx.equal(b.soln.p0, 100)697 approx.equal(b.soln.m0, 500)698 // Move point load off-center699 b.removePointLoad(ptLd)700 ptLd = b.addPointLoad(3, 60)701 b.solve(50)702 approx.equal(b.soln.p0, 60)703 approx.equal(b.soln.m0, 180)704 // Move anchor to other side705 b.anchorLeft = 'free'706 b.anchorRight = 'fixed'707 b.solve(50)708 approx.equal(b.soln.pL, 60)709 approx.equal(b.soln.mL, -420)710 // Replace point load with continuous load711 b.pointLoads = []712 b.contLoad = x => 1713 b.solve(50)714 approx.equal(b.soln.pL, 10)715 approx.equal(b.soln.mL, -50)716 // Move anchor to other side717 b.anchorLeft = 'fixed'718 b.anchorRight = 'free'719 b.solve(50)720 approx.equal(b.soln.p0, 10)721 approx.equal(b.soln.m0, 50)722 })723 it('should correctly calculate reactive loads with two pins', () => {724 // Beam with one point load725 let b = new Beam()726 b.length = 10727 b.moment = 10728 b.modulus = 10729 b.anchorLeft = 'free'730 b.anchorRight = 'free'731 b.addPin(0)732 b.addPin(10)733 b.addPointLoad(4, 100)734 b.solve(50)735 approx.equal(b.soln.pin0, 60)736 approx.equal(b.soln.pin1, 40)737 // Add a second point load738 b.addPointLoad(5, 50)739 b.solve(50)740 approx.equal(b.soln.pin0, 85)741 approx.equal(b.soln.pin1, 65)742 // Cantilevered beam743 b.pins = [{ x: 0 }, { x: 5 }]744 b.pointLoads = [{ x: 10, w: 100 }]745 b.anchorLeft = 'free'746 b.anchorRight = 'free'747 b.solve(50)748 approx.equal(b.soln.pin0, -100)749 approx.equal(b.soln.pin1, 200)750 // Continuous loads751 b.pointLoads = []752 b.contLoad = x => 1753 b.solve(50)754 approx.equal(b.soln.pin0, 0)755 approx.equal(b.soln.pin1, 10)756 })757 it('calculated values should satisfy boundary conditions', () => {758 let b = new Beam()759 b.length = 10760 b.moment = 3761 b.modulus = 3762 b.anchorLeft = 'free'763 b.anchorRight = 'free'764 b.addPin(4)765 b.addPin(6)766 b.addPointLoad(2, 1)767 b.addPointLoad(5, 2)768 b.addPointLoad(8, 1)769 b.solve(5)770 approx.equal(b.grid[0].v, 0)771 approx.equal(b.grid[0].m, 0)772 approx.equal(b.grid[b.grid.length - 1].v, 0)773 approx.equal(b.grid[b.grid.length - 1].m, 0)774 b.anchorLeft = 'fixed'775 b.anchorRight = 'fixed'776 b.pins = []777 b.solve(20)778 approx.equal(b.grid[0].v, 0)779 approx.equal(b.grid[0].m, 0)780 approx.equal(b.grid[0].theta, 0)781 approx.equal(b.grid[0].y, 0)782 approx.equal(b.grid[b.grid.length - 1].v, 0)783 approx.equal(b.grid[b.grid.length - 1].m, 0)784 approx.equal(b.grid[b.grid.length - 1].theta, 0)785 approx.equal(b.grid[b.grid.length - 1].y, 0)786 b.anchorLeft = 'free'787 b.anchorRight = 'fixed'788 b.pins = []789 b.solve(20)790 approx.equal(b.grid[0].v, 0)791 approx.equal(b.grid[0].m, 0)792 approx.equal(b.grid[b.grid.length - 1].v, 0)793 approx.equal(b.grid[b.grid.length - 1].m, 0)794 approx.equal(b.grid[b.grid.length - 1].theta, 0)795 approx.equal(b.grid[b.grid.length - 1].y, 0)796 b.anchorLeft = 'fixed'797 b.anchorRight = 'free'798 b.pins = []799 b.solve(20)800 approx.equal(b.grid[0].v, 0)801 approx.equal(b.grid[0].m, 0)802 approx.equal(b.grid[0].theta, 0)803 approx.equal(b.grid[0].y, 0)804 approx.equal(b.grid[b.grid.length - 1].v, 0)805 approx.equal(b.grid[b.grid.length - 1].m, 0)806 })807 it('should correctly calculate deflection in simply supported beam with uniform load', () => {808 let b = new Beam()809 b.length = 5810 b.modulus = 20811 b.moment = 30812 b.anchorLeft = 'free'813 b.anchorRight = 'free'814 b.addPin(0)815 b.addPin(b.length)816 b.contLoad = () => -4817 // Exact solution:818 // y(x) = -w x / (24 E I) * (x^3 - 2 L x^2 + L^3)819 b.solve(10)820 approx.equal(b.grid[0].y, 0)821 approx.equal(b.grid[2].y, -0.01703125)822 approx.equal(b.grid[4].y, -0.04411458333333334)823 approx.equal(b.grid[6].y, -0.054253472222222224)824 approx.equal(b.grid[8].y, -0.04411458333333334)825 approx.equal(b.grid[10].y, -0.01703125)826 approx.equal(b.grid[12].y, 0)827 })828 it('should correctly calculate deflection in simply supported beam with point load', () => {829 let b = new Beam()830 b.length = 5831 b.modulus = 20832 b.moment = 30833 b.anchorLeft = 'free'834 b.anchorRight = 'free'835 b.addPin(0)836 b.addPin(b.length)837 b.addPointLoad(2.5, -40)838 // Exact solution:839 // y(L/2) = p L^3 / (48 E I)840 b.solve(10)841 approx.equal(b.grid[0].y, 0)842 approx.equal(b.grid[6].y, -0.1736111111111111)843 approx.equal(b.grid[12].y, 0)844 })845 })...
Sudoku.js
Source:Sudoku.js
...95 const solvedData = []96 this._0to8(i => solvedData.push(model.data[i].slice()))97 const state = {data: solvedData}98 // Display results depending on whether the puzzle was actually solved or not.99 if (this._isSolved(model)) {100 state['solvedMessage'] = 'Puzzle successfully solved'101 } else {102 state['notSolvedMessage'] = 'Puzzle not solved'103 }104 this.setState(state)105 } catch (e) {106 // Uh oh, there was an error while solving. This could be due to an entry error by the user, or, face it, a bug.107 // Let's just display the error message for now.108 this.setState({solveError: "There was an error during solving. This could be due to an entry error, or, well, a bug. But be a sport and check what you entered."})109 console.log(e)110 }111 }112 clear() {113 const data = []114 this._1to9(i => {115 const row = []116 data.push(row)117 this._1to9(j => row.push(''))118 })119 this.setState({data})120 this.clearMessages()121 }122 hardest() {123 // World's hardest124 this.setState({data: [125 [ '8', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' ], //126 [ ' ', ' ', '3', '6', ' ', ' ', ' ', ' ', ' ' ], //127 [ ' ', '7', ' ', ' ', '9', ' ', '2', ' ', ' ' ], //128 [ ' ', '5', ' ', ' ', ' ', '7', ' ', ' ', ' ' ], //129 [ ' ', ' ', ' ', ' ', '4', '5', '7', ' ', ' ' ], //130 [ ' ', ' ', ' ', '1', ' ', ' ', ' ', '3', ' ' ], //131 [ ' ', ' ', '8', '5', ' ', ' ', ' ', '1', ' ' ], //132 [ ' ', ' ', '1', ' ', ' ', ' ', ' ', '6', '8' ], //133 [ ' ', '9', ' ', ' ', ' ', ' ', '4', ' ', ' ' ], //134 ]135 })136 this.clearMessages()137 }138 clearMessages() {139 this.setState({solvedMessage: null, notSolvedMessage: null, solveError: null})140 }141 render() {142 let solvedMessage, notSolvedMessage, solveError143 if (this.state.solvedMessage) {144 solvedMessage = <Alert color="success">{this.state.solvedMessage}</Alert>145 }146 if (this.state.notSolvedMessage) {147 solvedMessage = <Alert color="warning">{this.state.notSolvedMessage}</Alert>148 }149 if (this.state.solveError) {150 solvedMessage = <Alert color="danger">{this.state.solveError}</Alert>151 }152 return (153 <div className="text-center">154 <div id="sudoku">155 <table>156 <tbody>157 <tr>158 <Section d={this.state.data} r={0} c={0} f={this.updateCell}/>159 <Section d={this.state.data} r={0} c={1} f={this.updateCell}/>160 <Section d={this.state.data} r={0} c={2} f={this.updateCell}/>161 </tr>162 <tr>163 <Section d={this.state.data} r={1} c={0} f={this.updateCell}/>164 <Section d={this.state.data} r={1} c={1} f={this.updateCell}/>165 <Section d={this.state.data} r={1} c={2} f={this.updateCell}/>166 </tr>167 <tr>168 <Section d={this.state.data} r={2} c={0} f={this.updateCell}/>169 <Section d={this.state.data} r={2} c={1} f={this.updateCell}/>170 <Section d={this.state.data} r={2} c={2} f={this.updateCell}/>171 </tr>172 </tbody>173 </table>174 <div className='btns'>175 <Button color="primary" onClick={this.solve}>Solve!</Button>{' '}176 <Button color="secondary" onClick={this.clear}>Clear</Button>{' '}177 <Button color="secondary" onClick={this.hardest}>World's Hardest</Button>178 </div>179 {solvedMessage}180 {notSolvedMessage}181 {solveError}182 </div>183 </div>184 )185 }186 _1to9(fn) { this._xtoy(1, 10, fn) }187 _0to8(fn) { this._xtoy(0, 9, fn) }188 _xtoy(start, end, fn) {189 for (let i=start; i<end; i++) {190 fn(i)191 }192 }193 _1to9Sq(fn) { this._1to9(i => this._1to9(j => fn(i, j))) }194 _0to8Sq(fn) { this._0to8(i => this._0to8(j => fn(i, j))) }195 _xtoySq(start, end, fn) { this._xtoy(start, end, i => this._xtoy(start, end, j => fn(i, j))) }196 _solve(model) {197 let changed = false198 while (true) {199 changed = this._solveWithSolvers(model, this._firstSolvers)200 if (!this._isSolved(model)) {201 if (this._solveWithSolvers(model, this._secondSolvers)) {202 changed = true203 }204 }205 if (this._isSolved(model) || !changed) {206 break207 }208 changed = false209 }210 }211 _solveWithSolvers(model, solvers) {212 let changed = false213 model.changed = false214 while (true) {215 solvers.forEach(s => s(model))216 if (!model.changed) {217 break218 }219 changed = true220 model.changed = false221 }222 return changed223 }224 _isCellSolved(model, x, y) {225 return model.data[x][y].length === 1226 }227 _getSolvedValue(model, x, y) {228 return model.data[x][y][0]229 }230 _isSolved(model) {231 for (let y = 0; y < SIZE; y++) {232 for (let x = 0; x < SIZE; x++) {233 if (!this._isCellSolved(model, x, y)) {234 return false235 }236 }237 }238 return true239 }240 _removeValues(model, x, y, remove) {241 const values = model.data[x][y]242 remove.forEach(e => {243 let pos = values.indexOf(e)244 if (pos >= 0) {245 values.splice(pos, 1)246 model.changed = true247 }248 })249 }250 _setValue(model, x, y, value) {251 if (model.data[x][y].indexOf(value) === -1) {252 throw Error("Attempt to set cell to a value that has already been eliminated: cell:" + model.data[x][y] + ", value=" + value)253 }254 // Check if the cell will actually change.255 if (model.data[x][y].length === 1 && model.data[x][y][0] === value) {256 return257 }258 model.data[x][y] = [value]259 model.changed = true260 }261 //262 // Solvers263 //264 _boxEliminator(model) {265 // For each section266 this._xtoySq(0, BOX_SIZE, (i, j) => {267 const xStart = i * BOX_SIZE268 const yStart = j * BOX_SIZE269 // Collect a list of all solved cells.270 const solved = []271 this._xtoy(xStart, BOX_SIZE + xStart, x => {272 this._xtoy(yStart, BOX_SIZE + yStart, y => {273 if (this._isCellSolved(model, x, y)) {274 solved.push(this._getSolvedValue(model, x, y))275 }276 })277 })278 // Remove these solved values from unsolved cells.279 this._xtoy(xStart, BOX_SIZE + xStart, x => {280 this._xtoy(yStart, BOX_SIZE + yStart, y => {281 if (!this._isCellSolved(model, x, y)) {282 this._removeValues(model, x, y, solved)283 }284 })285 })286 })287 }288 _verticalEliminator(model) {289 this._0to8(col => {290 // Collect a list of all solved cells.291 const solved = []292 this._0to8(i => {293 if (this._isCellSolved(model, i, col)) {294 solved.push(this._getSolvedValue(model, i, col))295 }296 })297 // Remove these solved values from unsolved cells.298 this._0to8(i => {299 if (!this._isCellSolved(model, i, col)) {300 this._removeValues(model, i, col, solved)301 }302 })303 })304 }305 _horizontalEliminator(model) {306 this._0to8(row => {307 // Collect a list of all solved cells.308 const solved = []309 this._0to8(i => {310 if (this._isCellSolved(model, row, i)) {311 solved.push(this._getSolvedValue(model, row, i))312 }313 })314 // Remove these solved values from unsolved cells.315 this._0to8(i => {316 if (!this._isCellSolved(model, row, i)) {317 this._removeValues(model, row, i, solved)318 }319 })320 })321 }322 _boxSingleValueSeeker(model) {323 const FOUND_POINT = -1324 this._xtoySq(0, BOX_SIZE, (i, j) => {325 const xStart = i * BOX_SIZE326 const yStart = j * BOX_SIZE327 this._1to9(value => {328 let point = null329 this._xtoy(xStart, BOX_SIZE + xStart, x => {330 this._xtoy(yStart, BOX_SIZE + yStart, y => {331 if (model.data[x][y].indexOf(value) !== -1) {332 if (point == null) {333 point = [x, y]334 } else {335 point = FOUND_POINT336 }337 }338 })339 })340 if (point === null) {341 throw Error("No cell can take the value " + value + ", box at " + xStart + "," + yStart)342 }343 if (point !== FOUND_POINT && !this._isCellSolved(model, point[0], point[1])) {344 // Only one cell can take the value, so set the cell to that value.345 this._setValue(model, point[0], point[1], value)346 }347 })348 })349 }350 _horizontalSingleValueSeeker(model) {351 this._0to8Sq((row, v) => {352 const value = v + 1353 let column = -1354 this._0to8(i => {355 if (model.data[i][row].indexOf(value) !== -1) {356 if (column === -1) {357 column = i358 } else {359 column = -2360 }361 }362 })363 if (column === -1) {364 throw Error("No cell can take the value " + value +", row " + row)365 }366 if (column > -1 && !this._isCellSolved(model, column, row)) {367 // Only one cell can take the value, so set the cell to that value.368 this._setValue(model, column, row, value)369 }370 })371 }372 _verticalSingleValueSeeker(model) {373 this._0to8Sq((col, v) => {374 const value = v + 1375 let row = -1376 this._0to8(i => {377 if (model.data[col][i].indexOf(value) !== -1) {378 if (row === -1) {379 row = i380 } else {381 row = -2382 }383 }384 })385 if (row === -1) {386 throw Error("No cell can take the value " + value +", column " + col)387 }388 if (row > -1 && !this._isCellSolved(model, col, row)) {389 // Only one cell can take the value, so set the cell to that value.390 this._setValue(model, col, row, value)391 }392 })393 }394 _potentialValueElimination(model) {395 try {396 // Iterate through the cells looking for those with multiple potential values.397 this._0to8Sq((x, y) => {398 if (!this._isCellSolved(model, x, y)) {399 const values = model.data[x][y].slice()400 // Test the values in a clone.401 values.forEach(value => {402 const dataClone = []403 this._0to8(i => {404 const row = []405 dataClone.push(row)406 this._0to8(j => row.push(model.data[i][j].slice()))407 })408 const modelClone = {data: dataClone, changed: false}409 this._setValue(modelClone, x, y, value)410 let err = false411 try {412 this._solve(modelClone)413 } catch (e) {414 // The value didn't work, so remove it as a potential value.415 this._removeValues(model, x, y, [value])416 err = true417 if (model.data[x][y].length === 0) {418 throw Error("No values remain at " + x + ", " + y)419 }420 }421 if (!err) {422 if (this._isSolved(modelClone)) {423 model.data = modelClone.data424 model.changed = modelClone.changed425 // Throw an error to escape from the loops. This is caught below.426 throw Error("solved")427 }428 }429 })430 }431 })432 } catch (e) {433 // Catches the solved exception. Rethrows all others.434 if (e.message !== "solved") {435 throw e436 }...
main.js
Source:main.js
1 // #### SEPERATOR #####2 // Converts global co-ords used in mouse events to relative to element3 function relMouseCoords(event) {4 var totalOffsetX = 0;5 var totalOffsetY = 0;6 var canvasX = 0;7 var canvasY = 0;8 var currentElement = this;9 do {10 totalOffsetX += currentElement.offsetLeft;11 totalOffsetY += currentElement.offsetTop;12 }13 while (currentElement = currentElement.offsetParent)14 canvasX = event.pageX - totalOffsetX;15 canvasY = event.pageY - totalOffsetY;16 return { x: canvasX, y: canvasY }17 }18 HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;19 // Javascript doesn't have 'contains' so added here for later readability20 Array.prototype.contains = function (element) {21 for (var i = 0; i < this.length; i++) {22 if (this[i] == element) {23 return true;24 }25 }26 return false;27 }28 var SquareSize = 3;29 var BoardSize = SquareSize * SquareSize;30 function AllowedValues(n) {31 this._mask = n;32 }33 AllowedValues.prototype.getSingle = function () {34 // Count number of on bits from 1..935 var single = 0;36 var count = 0;37 for (var i = 1; i <= BoardSize; i++)38 if ((this._mask & (1 << i)) != 0) {39 count++;40 single = i;41 }42 return count == 1 ? single : 0;43 };44 // Used when the answer is known at the Cell, level this sets the only allowed value to be that answer45 AllowedValues.prototype.setSingle = function (n) {46 this._mask = 1 << n;47 };48 AllowedValues.prototype.count = function () {49 // Count number of on bits from 1..950 var count = 0;51 for (var i = 1; i <= BoardSize; i++)52 if ((this._mask & (1 << i)) != 0)53 count++;54 return count;55 };56 AllowedValues.prototype.isAllowed = function (n) {57 return n >= 1 && n <= BoardSize && ((this._mask & (1 << n)) != 0);58 };59 AllowedValues.prototype.removeValues = function (bm) {60 this._mask &= ~bm._mask;61 };62 AllowedValues.prototype.allowedValuesArray = function () {63 var ret = new Array();64 for (var i = 1; i <= BoardSize; i++)65 if (((1 << i) & this._mask) != 0)66 ret.push(i);67 return ret;68 };69 AllowedValues.prototype.clone = function () {70 return new AllowedValues(this._mask);71 };72 function Cell(value) {73 this._value = value; // 0 means unassigned74 this._allowed = new AllowedValues(0x3e); // all possible75 this._answer = 0; // no answer76 this._given = false;77 }78 Cell.prototype.clone = function (value) {79 var clone = new Cell();80 clone._value = this._value;81 clone._allowed = this._allowed.clone();82 clone._answer = this._answer;83 clone._given = this._given;84 return clone;85 };86 Cell.prototype.single = function (value) {87 this._value = value; // value user (or auto solve functions) has assigned as a possible answer88 this._allowed = new AllowedValues(0x3e); // the allowed values as a bit mask89 this._answer = 0; // calculated as the only possible correct value90 };91 Cell.prototype.valueMask = function () {92 return this._value == 0 ? 0 : 1 << this._value;93 };94 Cell.prototype.hasAnswer = function () {95 return this._answer != 0;96 };97 Cell.prototype.getAnswer = function () {98 return this._answer;99 };100 Cell.prototype.setAnswer = function (n) {101 if (n < 0 || n > 9)102 throw "Illegal value not in the range 1..9.";103 this._answer = n;104 };105 Cell.prototype.getValue = function () {106 return this._value;107 };108 Cell.prototype.setValue = function (n) {109 if (n < 0 || n > 9)110 throw "Illegal value not in the range 1..9.";111 if (n != 0 && !this._allowed.isAllowed(n))112 throw "Not allowed.";113 this._value = n;114 this._given = false;115 };116 Cell.prototype.setGiven = function (n) {117 if (n < 0 || n > 9)118 throw "Illegal value not in the range 1..9.";119 this._value = n;120 this._given = n != 0;121 this._answer = 0;122 };123 Cell.prototype.isGiven = function () {124 return this._given;125 };126 Cell.prototype.isAssigned = function () {127 return this._value != 0;128 };129 Cell.prototype.clear = function () {130 this._value = 0; // means unassigned131 this._allowed = new AllowedValues(0x3E); // all possible132 this._answer = 0;133 this._given = 0;134 };135 Cell.prototype.isAllowed = function (value) {136 return this._allowed.isAllowed(value);137 };138 Cell.prototype.setAllowed = function (value) {139 this._allowed = new AllowedValues(value);140 };141 Cell.prototype.getAllowedClone = function (value) {142 return this._allowed.clone();143 };144 var SibType = { "Row": 1, "Col": 2, "Square": 3 };145 function Location(row, col) {146 this.row = row;147 this.col = col;148 }149 Location.empty = new Location(-1, -1);150 Location.prototype.isEmpty = function () {151 return this.row < 0;152 };153 Location.prototype.modulo = function (n) {154 if (n < 0)155 return n + BoardSize;156 return n % BoardSize;157 };158 Location.prototype.left = function () {159 return new Location(this.row, this.modulo(this.col - 1));160 };161 Location.prototype.right = function () {162 return new Location(this.row, this.modulo(this.col + 1));163 };164 Location.prototype.up = function () {165 return new Location(this.modulo(this.row - 1), this.col);166 };167 Location.prototype.down = function () {168 return new Location(this.modulo(this.row + 1), this.col);169 };170 Location.prototype.toString = function () {171 return "Row=" + String(this.row) + "Col=" + String(this.col);172 };173 Location.prototype.getSquare = function () {174 return 3 * Math.floor(this.row / 3) + Math.floor(this.col / 3);175 };176 Location.prototype.equals = function (a) {177 return a.row == this.row && a.col == this.col;178 };179 Location.prototype.notEquals = function (a) {180 return a.row != this.row || a.col != this.col;181 };182 // Enumerator for locations of all cells183 Location.grid = function () {184 var locs = new Array();185 for (var i = 0; i < BoardSize; i++)186 for (var j = 0; j < BoardSize; j++)187 locs.push(new Location(i, j));188 return locs;189 };190 // Enumerator for locations of cell siblings in the same row191 Location.prototype.rowSibs = function () {192 var locs = new Array();193 for (var i = 0; i < BoardSize; i++)194 if (i != this.col)195 locs.push(new Location(this.row, i));196 return locs;197 };198 // Enumerator for locations of cell siblings in the same column199 Location.prototype.colSibs = function () {200 var locs = new Array();201 for (var i = 0; i < BoardSize; i++)202 if (i != this.row)203 locs.push(new Location(i, this.col));204 return locs;205 };206 // Enumerator for locations of cell siblings in the same square207 Location.prototype.squareSibs = function () {208 var locs = new Array();209 var baseRow = 3 * Math.floor(this.row / 3); // this is how to convert float to an "int" - Javascript doesn't have ints!210 var baseCol = 3 * Math.floor(this.col / 3);211 for (var i = 0; i < SquareSize; i++) {212 var r = baseRow + i;213 for (var j = 0; j < SquareSize; j++) {214 var c = baseCol + j;215 if (r != this.row || c != this.col)216 locs.push(new Location(r, c));217 }218 }219 return locs;220 };221 Location.prototype.getSibs = function (type) {222 switch (type) {223 case SibType.Row:224 return this.rowSibs();225 case SibType.Col:226 return this.colSibs();227 case SibType.Square:228 return this.squareSibs();229 }230 };231 function Board() {232 function MultiDimArray(rows, cols) {233 var a = new Array(rows);234 for (var i = 0; i < rows; i++) {235 a[i] = new Array(cols);236 for (var j = 0; j < cols; j++)237 a[i][j] = new Cell();238 }239 return a;240 }241 this._digits = MultiDimArray(BoardSize, BoardSize);242 this._isSolved = false;243 this._isValid = false;244 }245 Board.prototype.clone = function () {246 var clone = new Board();247 clone._isSolved = this._isSolved;248 clone._isValid = this._isValid;249 clone._digits = new Array(BoardSize);250 for (var i = 0; i < BoardSize; i++) {251 clone._digits[i] = new Array(BoardSize);252 for (var j = 0; j < BoardSize; j++)253 clone._digits[i][j] = this._digits[i][j].clone();254 }255 return clone;256 };257 Board.prototype.copyTo = function (target) {258 target._isSolved = this._isSolved;259 target._isValid = this._isValid;260 for (var i = 0; i < BoardSize; i++)261 for (var j = 0; j < BoardSize; j++)262 target._digits[i][j] = this._digits[i][j].clone();263 };264 Board.prototype.getCell = function (loc) {265 return this._digits[loc.row][loc.col];266 };267 Board.prototype.setCell = function (loc, value) {268 this._digits[loc.row][loc.col] = value;269 };270 Board.prototype.clear = function () {271 for (var i = 0; i < BoardSize; i++)272 for (var j = 0; j < BoardSize; j++)273 this._digits[i][j].clear();274 this.updateAllowed();275 };276 Board.prototype.reset = function () {// return Baord to only the givens277 for (var i = 0; i < BoardSize; i++)278 for (var j = 0; j < BoardSize; j++) {279 var cell = this._digits[i][j];280 if (!cell.isGiven())281 cell.clear();282 }283 this.updateAllowed();284 };285 Board.prototype.checkIsValidSibs = function (loc, digit, locs) {286 for (var i = 0; i < locs.length; i++) {287 var loc = locs[i];288 var cell = this._digits[loc.row][loc.col];289 if (cell.getAnswer() == digit)290 return false;291 }292 return true;293 };294 Board.prototype.checkIsValid = function (loc, digit) {295 // Checks if the digit can go in that location by checking it doesn't296 // exist in either the row, col or square siblings297 if (!this.checkIsValidSibs(loc, digit, loc.colSibs()))298 return false;299 if (!this.checkIsValidSibs(loc, digit, loc.rowSibs()))300 return false;301 if (!this.checkIsValidSibs(loc, digit, loc.squareSibs()))302 return false;303 return true;304 };305 Board.prototype.acceptPossibles = function () {306 var more = false;307 var locs = Location.grid();308 for (var i = 0; i < locs.length; i++) {309 var loc = locs[i];310 var cell = this._digits[loc.row][loc.col];311 if (!cell.isAssigned() && cell.hasAnswer() && this.checkIsValid(loc, cell.getAnswer())) {312 cell.setValue(cell.getAnswer()); // if unassigned and has the answer then assign the answer313 more = true;314 }315 }316 return more;317 };318 Board.prototype.checkForHiddenSingles = function (loc, st) {319 // Check each cell - if not assigned and has no answer then check its siblings320 // get all its allowed then remove all the allowed321 var cell = this.getCell(loc);322 if (!cell.isAssigned() && !cell.hasAnswer()) {323 var allowed = cell.getAllowedClone(); // copy of bit mask of allowed values for this cell324 var locs = loc.getSibs(st);325 for (var i = 0; i < locs.length; i++) {326 var sib = locs[i];327 var sibCell = this.getCell(sib);328 if (!sibCell.isAssigned())329 allowed.removeValues(sibCell.getAllowedClone()); // remove allowed values from siblings330 }331 var answer = allowed.getSingle(); // if there is only one allowed value left (i.e. this cell is the only one amonsgt its sibs with this allowed value)332 // then apply it as the answer. Note getSingle will return 0 (i.e. no anser) if the number of allowed values is not exactly one333 if (answer != 0) {334 cell.setAnswer(answer);335 return true; // no need to check others sibling collections336 }337 }338 return false;339 };340 Board.prototype.findCellWithFewestChoices = function () {341 var minLocation = Location.empty;342 var minCount = 9;343 var locs = Location.grid();344 for (var i = 0; i < locs.length; i++) {345 var loc = locs[i];346 var cell = this.getCell(loc);347 if (!cell.isAssigned()) {348 var count = cell.getAllowedClone().count();349 if (count < minCount) {350 minLocation = loc;351 minCount = count;352 }353 }354 }355 return minLocation;356 };357 Board.prototype.updateAllowed = function () {358 // Called whenever the user sets a value or via auto solve359 // Updates the allowed values for each cell based on existing digits360 // entered in a cell's row, col or square361 var cols = new Array(BoardSize);362 var rows = new Array(BoardSize);363 var squares = new Array(BoardSize);364 // First aggregate assigned values to rows, cols, squares365 var locs = Location.grid();366 for (var i = 0; i < locs.length; i++) {367 var loc = locs[i];368 // Disallow for all cells in this row369 var contains = this.getCell(loc).valueMask();370 rows[loc.row] |= contains;371 cols[loc.col] |= contains;372 squares[loc.getSquare()] |= contains;373 }374 // For each cell, aggregate the values already set in that row, col and square.375 // Since the aggregate is a bitmask, the bitwise inverse of that is therefore the allowed values.376 this._isValid = true;377 this._isSolved = true;378 for (var i = 0; i < locs.length; i++) {379 var loc = locs[i];380 // Set allowed values381 var contains = rows[loc.row] | cols[loc.col] | squares[loc.getSquare()];382 var cell = this.getCell(loc);383 cell.setAllowed(~contains); // set allowed values to what values are not already set in this row, col or square384 cell.setAnswer(0); //clear any previous answers385 // As an extra step look for "naked singles", i.e. cells that have only one allowed value, and use386 // that to set the answer (note this is different from the "value" as this can only be assigned387 // by the user or any auto solve functions like "accept singles"388 if (!cell.isAssigned()) {389 this._isSolved = false;390 var mask = new AllowedValues(~contains);391 var count = mask.count();392 if (count == 0)393 this._isValid = false;394 else if (count == 1)395 cell.setAnswer(mask.getSingle());396 }397 }398 // Step 2: Look for "hidden singles".399 // For each row, col, square, count number of times each digit appears.400 // If any appear once then set that as the answer for that cell.401 // Count in rows402 for (var i = 0; i < locs.length; i++) {403 var loc = locs[i];404 if (!this.checkForHiddenSingles(loc, SibType.Row))// first check row sibs for a hiddne single405 if (!this.checkForHiddenSingles(loc, SibType.Col))// then check cols406 this.checkForHiddenSingles(loc, SibType.Square); // then check square407 }408 // TO DO: Add code here to detect naked/hidden doubles/triples/quads409 };410 Board.prototype.trySolve = function (loc, value) {// empty Location allowed411 if (!loc.isEmpty())// assign a value to a location if provided412 {413 var cell = this.getCell(loc);414 if (!cell.isAllowed(value))415 throw "Internal error.";416 cell.setValue(value);417 }418 do {419 this.updateAllowed();420 if (!this._isValid)421 return false;422 } while (this.acceptPossibles()); // keep doing deterministic answers423 if (this._isSolved)424 return true;425 if (!this._isValid)426 return false;427 // No deterministic solutions, find cell with the fewest choices and try each one in turn428 // until success. 429 var locChoice = this.findCellWithFewestChoices();430 if (locChoice.isEmpty())431 return false;432 var cell = this.getCell(locChoice);433 var allowedValues = cell._allowed.allowedValuesArray();434 for (var i = 0; i < allowedValues.length; i++) {435 var val = allowedValues[i];436 var board = this.clone();437 if (board.trySolve(locChoice, val)) {438 board.copyTo(this);439 return true;440 }441 }442 return false;443 };444 Board.prototype.toString = function () {445 var text = "";446 for (var row = 0; row < BoardSize; row++)447 for (var col = 0; col < BoardSize; col++) {448 var val = this._digits[row][col].getValue();449 text += val == 0 ? "." : String(val);450 }451 return text;452 };453 Board.prototype.setString = function (value) {454 // Assumes all input is digits 1..9 or ./space455 if (value.length != (BoardSize * BoardSize))456 return false; //Input string is not of length 81457 var n = 0;458 for (var row = 0; row < BoardSize; row++)459 for (var col = 0; col < BoardSize; col++) {460 var ch = parseInt(value.charAt(n++)); // converts '0' to 0 etc461 var cell = this._digits[row][col];462 cell.setGiven(!isNaN(ch) ? ch : 0);463 }464 this.updateAllowed();465 return true;...
logic.js
Source:logic.js
1function relMouseCoords(event) {2 var totalOffsetX = 0;3 var totalOffsetY = 0;4 var canvasX = 0;5 var canvasY = 0;6 var currentElement = this;7 do {8 totalOffsetX += currentElement.offsetLeft;9 totalOffsetY += currentElement.offsetTop;10 }11 while (currentElement = currentElement.offsetParent)12 canvasX = event.pageX - totalOffsetX;13 canvasY = event.pageY - totalOffsetY;14 return { x: canvasX, y: canvasY }15 }16 HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;17 // Javascript doesn't have 'contains' so added here for later readability18 Array.prototype.contains = function (element) {19 for (var i = 0; i < this.length; i++) {20 if (this[i] == element) {21 return true;22 }23 }24 return false;25 }26 var SquareSize = 3;27 var BoardSize = SquareSize * SquareSize;28 function AllowedValues(n) {29 this._mask = n;30 }31 AllowedValues.prototype.getSingle = function () {32 // Count number of on bits from 1..933 var single = 0;34 var count = 0;35 for (var i = 1; i <= BoardSize; i++)36 if ((this._mask & (1 << i)) != 0) {37 count++;38 single = i;39 }40 return count == 1 ? single : 0;41 };42 // Used when the answer is known at the Cell, level this sets the only allowed value to be that answer43 AllowedValues.prototype.setSingle = function (n) {44 this._mask = 1 << n;45 };46 AllowedValues.prototype.count = function () {47 // Count number of on bits from 1..948 var count = 0;49 for (var i = 1; i <= BoardSize; i++)50 if ((this._mask & (1 << i)) != 0)51 count++;52 return count;53 };54 AllowedValues.prototype.isAllowed = function (n) {55 return n >= 1 && n <= BoardSize && ((this._mask & (1 << n)) != 0);56 };57 AllowedValues.prototype.removeValues = function (bm) {58 this._mask &= ~bm._mask;59 };60 AllowedValues.prototype.allowedValuesArray = function () {61 var ret = new Array();62 for (var i = 1; i <= BoardSize; i++)63 if (((1 << i) & this._mask) != 0)64 ret.push(i);65 return ret;66 };67 AllowedValues.prototype.clone = function () {68 return new AllowedValues(this._mask);69 };70 function Cell(value) {71 this._value = value; // 0 means unassigned72 this._allowed = new AllowedValues(0x3e); // all possible73 this._answer = 0; // no answer74 this._given = false;75 }76 Cell.prototype.clone = function (value) {77 var clone = new Cell();78 clone._value = this._value;79 clone._allowed = this._allowed.clone();80 clone._answer = this._answer;81 clone._given = this._given;82 return clone;83 };84 Cell.prototype.single = function (value) {85 this._value = value; // value user (or auto solve functions) has assigned as a possible answer86 this._allowed = new AllowedValues(0x3e); // the allowed values as a bit mask87 this._answer = 0; // calculated as the only possible correct value88 };89 Cell.prototype.valueMask = function () {90 return this._value == 0 ? 0 : 1 << this._value;91 };92 Cell.prototype.hasAnswer = function () {93 return this._answer != 0;94 };95 Cell.prototype.getAnswer = function () {96 return this._answer;97 };98 Cell.prototype.setAnswer = function (n) {99 if (n < 0 || n > 9)100 throw "Illegal value not in the range 1..9.";101 this._answer = n;102 };103 Cell.prototype.getValue = function () {104 return this._value;105 };106 Cell.prototype.setValue = function (n) {107 if (n < 0 || n > 9)108 throw "Illegal value not in the range 1..9.";109 if (n != 0 && !this._allowed.isAllowed(n))110 throw "Not allowed.";111 this._value = n;112 this._given = false;113 };114 Cell.prototype.setGiven = function (n) {115 if (n < 0 || n > 9)116 throw "Illegal value not in the range 1..9.";117 this._value = n;118 this._given = n != 0;119 this._answer = 0;120 };121 Cell.prototype.isGiven = function () {122 return this._given;123 };124 Cell.prototype.isAssigned = function () {125 return this._value != 0;126 };127 Cell.prototype.clear = function () {128 this._value = 0; // means unassigned129 this._allowed = new AllowedValues(0x3E); // all possible130 this._answer = 0;131 this._given = 0;132 };133 Cell.prototype.isAllowed = function (value) {134 return this._allowed.isAllowed(value);135 };136 Cell.prototype.setAllowed = function (value) {137 this._allowed = new AllowedValues(value);138 };139 Cell.prototype.getAllowedClone = function (value) {140 return this._allowed.clone();141 };142 var SibType = { "Row": 1, "Col": 2, "Square": 3 };143 function Location(row, col) {144 this.row = row;145 this.col = col;146 }147 Location.empty = new Location(-1, -1);148 Location.prototype.isEmpty = function () {149 return this.row < 0;150 };151 Location.prototype.modulo = function (n) {152 if (n < 0)153 return n + BoardSize;154 return n % BoardSize;155 };156 Location.prototype.left = function () {157 return new Location(this.row, this.modulo(this.col - 1));158 };159 Location.prototype.right = function () {160 return new Location(this.row, this.modulo(this.col + 1));161 };162 Location.prototype.up = function () {163 return new Location(this.modulo(this.row - 1), this.col);164 };165 Location.prototype.down = function () {166 return new Location(this.modulo(this.row + 1), this.col);167 };168 Location.prototype.toString = function () {169 return "Row=" + String(this.row) + "Col=" + String(this.col);170 };171 Location.prototype.getSquare = function () {172 return 3 * Math.floor(this.row / 3) + Math.floor(this.col / 3);173 };174 Location.prototype.equals = function (a) {175 return a.row == this.row && a.col == this.col;176 };177 Location.prototype.notEquals = function (a) {178 return a.row != this.row || a.col != this.col;179 };180 // Enumerator for locations of all cells181 Location.grid = function () {182 var locs = new Array();183 for (var i = 0; i < BoardSize; i++)184 for (var j = 0; j < BoardSize; j++)185 locs.push(new Location(i, j));186 return locs;187 };188 // Enumerator for locations of cell siblings in the same row189 Location.prototype.rowSibs = function () {190 var locs = new Array();191 for (var i = 0; i < BoardSize; i++)192 if (i != this.col)193 locs.push(new Location(this.row, i));194 return locs;195 };196 // Enumerator for locations of cell siblings in the same column197 Location.prototype.colSibs = function () {198 var locs = new Array();199 for (var i = 0; i < BoardSize; i++)200 if (i != this.row)201 locs.push(new Location(i, this.col));202 return locs;203 };204 // Enumerator for locations of cell siblings in the same square205 Location.prototype.squareSibs = function () {206 var locs = new Array();207 var baseRow = 3 * Math.floor(this.row / 3); // this is how to convert float to an "int" - Javascript doesn't have ints!208 var baseCol = 3 * Math.floor(this.col / 3);209 for (var i = 0; i < SquareSize; i++) {210 var r = baseRow + i;211 for (var j = 0; j < SquareSize; j++) {212 var c = baseCol + j;213 if (r != this.row || c != this.col)214 locs.push(new Location(r, c));215 }216 }217 return locs;218 };219 Location.prototype.getSibs = function (type) {220 switch (type) {221 case SibType.Row:222 return this.rowSibs();223 case SibType.Col:224 return this.colSibs();225 case SibType.Square:226 return this.squareSibs();227 }228 };229 function Board() {230 function MultiDimArray(rows, cols) {231 var a = new Array(rows);232 for (var i = 0; i < rows; i++) {233 a[i] = new Array(cols);234 for (var j = 0; j < cols; j++)235 a[i][j] = new Cell();236 }237 return a;238 }239 this._digits = MultiDimArray(BoardSize, BoardSize);240 this._isSolved = false;241 this._isValid = false;242 }243 Board.prototype.clone = function () {244 var clone = new Board();245 clone._isSolved = this._isSolved;246 clone._isValid = this._isValid;247 clone._digits = new Array(BoardSize);248 for (var i = 0; i < BoardSize; i++) {249 clone._digits[i] = new Array(BoardSize);250 for (var j = 0; j < BoardSize; j++)251 clone._digits[i][j] = this._digits[i][j].clone();252 }253 return clone;254 };255 Board.prototype.copyTo = function (target) {256 target._isSolved = this._isSolved;257 target._isValid = this._isValid;258 for (var i = 0; i < BoardSize; i++)259 for (var j = 0; j < BoardSize; j++)260 target._digits[i][j] = this._digits[i][j].clone();261 };262 Board.prototype.getCell = function (loc) {263 return this._digits[loc.row][loc.col];264 };265 Board.prototype.setCell = function (loc, value) {266 this._digits[loc.row][loc.col] = value;267 };268 Board.prototype.clear = function () {269 for (var i = 0; i < BoardSize; i++)270 for (var j = 0; j < BoardSize; j++)271 this._digits[i][j].clear();272 this.updateAllowed();273 };274 Board.prototype.reset = function () {// return Baord to only the givens275 for (var i = 0; i < BoardSize; i++)276 for (var j = 0; j < BoardSize; j++) {277 var cell = this._digits[i][j];278 if (!cell.isGiven())279 cell.clear();280 }281 this.updateAllowed();282 };283 Board.prototype.checkIsValidSibs = function (loc, digit, locs) {284 for (var i = 0; i < locs.length; i++) {285 var loc = locs[i];286 var cell = this._digits[loc.row][loc.col];287 if (cell.getAnswer() == digit)288 return false;289 }290 return true;291 };292 Board.prototype.checkIsValid = function (loc, digit) {293 // Checks if the digit can go in that location by checking it doesn't294 // exist in either the row, col or square siblings295 if (!this.checkIsValidSibs(loc, digit, loc.colSibs()))296 return false;297 if (!this.checkIsValidSibs(loc, digit, loc.rowSibs()))298 return false;299 if (!this.checkIsValidSibs(loc, digit, loc.squareSibs()))300 return false;301 return true;302 };303 Board.prototype.acceptPossibles = function () {304 var more = false;305 var locs = Location.grid();306 for (var i = 0; i < locs.length; i++) {307 var loc = locs[i];308 var cell = this._digits[loc.row][loc.col];309 if (!cell.isAssigned() && cell.hasAnswer() && this.checkIsValid(loc, cell.getAnswer())) {310 cell.setValue(cell.getAnswer()); // if unassigned and has the answer then assign the answer311 more = true;312 }313 }314 return more;315 };316 Board.prototype.checkForHiddenSingles = function (loc, st) {317 // Check each cell - if not assigned and has no answer then check its siblings318 // get all its allowed then remove all the allowed319 var cell = this.getCell(loc);320 if (!cell.isAssigned() && !cell.hasAnswer()) {321 var allowed = cell.getAllowedClone(); // copy of bit mask of allowed values for this cell322 var locs = loc.getSibs(st);323 for (var i = 0; i < locs.length; i++) {324 var sib = locs[i];325 var sibCell = this.getCell(sib);326 if (!sibCell.isAssigned())327 allowed.removeValues(sibCell.getAllowedClone()); // remove allowed values from siblings328 }329 var answer = allowed.getSingle(); // if there is only one allowed value left (i.e. this cell is the only one amonsgt its sibs with this allowed value)330 // then apply it as the answer. Note getSingle will return 0 (i.e. no anser) if the number of allowed values is not exactly one331 if (answer != 0) {332 cell.setAnswer(answer);333 return true; // no need to check others sibling collections334 }335 }336 return false;337 };338 Board.prototype.findCellWithFewestChoices = function () {339 var minLocation = Location.empty;340 var minCount = 9;341 var locs = Location.grid();342 for (var i = 0; i < locs.length; i++) {343 var loc = locs[i];344 var cell = this.getCell(loc);345 if (!cell.isAssigned()) {346 var count = cell.getAllowedClone().count();347 if (count < minCount) {348 minLocation = loc;349 minCount = count;350 }351 }352 }353 return minLocation;354 };355 Board.prototype.updateAllowed = function () {356 // Called whenever the user sets a value or via auto solve357 // Updates the allowed values for each cell based on existing digits358 // entered in a cell's row, col or square359 var cols = new Array(BoardSize);360 var rows = new Array(BoardSize);361 var squares = new Array(BoardSize);362 // First aggregate assigned values to rows, cols, squares363 var locs = Location.grid();364 for (var i = 0; i < locs.length; i++) {365 var loc = locs[i];366 // Disallow for all cells in this row367 var contains = this.getCell(loc).valueMask();368 rows[loc.row] |= contains;369 cols[loc.col] |= contains;370 squares[loc.getSquare()] |= contains;371 }372 // For each cell, aggregate the values already set in that row, col and square.373 // Since the aggregate is a bitmask, the bitwise inverse of that is therefore the allowed values.374 this._isValid = true;375 this._isSolved = true;376 for (var i = 0; i < locs.length; i++) {377 var loc = locs[i];378 // Set allowed values379 var contains = rows[loc.row] | cols[loc.col] | squares[loc.getSquare()];380 var cell = this.getCell(loc);381 cell.setAllowed(~contains); // set allowed values to what values are not already set in this row, col or square382 cell.setAnswer(0); //clear any previous answers383 // As an extra step look for "naked singles", i.e. cells that have only one allowed value, and use384 // that to set the answer (note this is different from the "value" as this can only be assigned385 // by the user or any auto solve functions like "accept singles"386 if (!cell.isAssigned()) {387 this._isSolved = false;388 var mask = new AllowedValues(~contains);389 var count = mask.count();390 if (count == 0)391 this._isValid = false;392 else if (count == 1)393 cell.setAnswer(mask.getSingle());394 }395 }396 // Step 2: Look for "hidden singles".397 // For each row, col, square, count number of times each digit appears.398 // If any appear once then set that as the answer for that cell.399 // Count in rows400 for (var i = 0; i < locs.length; i++) {401 var loc = locs[i];402 if (!this.checkForHiddenSingles(loc, SibType.Row))// first check row sibs for a hiddne single403 if (!this.checkForHiddenSingles(loc, SibType.Col))// then check cols404 this.checkForHiddenSingles(loc, SibType.Square); // then check square405 }406 // TO DO: Add code here to detect naked/hidden doubles/triples/quads407 };408 Board.prototype.trySolve = function (loc, value) {// empty Location allowed409 if (!loc.isEmpty())// assign a value to a location if provided410 {411 var cell = this.getCell(loc);412 if (!cell.isAllowed(value))413 throw "Internal error.";414 cell.setValue(value);415 }416 do {417 this.updateAllowed();418 if (!this._isValid)419 return false;420 } while (this.acceptPossibles()); // keep doing deterministic answers421 if (this._isSolved)422 return true;423 if (!this._isValid)424 return false;425 // No deterministic solutions, find cell with the fewest choices and try each one in turn426 // until success. 427 var locChoice = this.findCellWithFewestChoices();428 if (locChoice.isEmpty())429 return false;430 var cell = this.getCell(locChoice);431 var allowedValues = cell._allowed.allowedValuesArray();432 for (var i = 0; i < allowedValues.length; i++) {433 var val = allowedValues[i];434 var board = this.clone();435 if (board.trySolve(locChoice, val)) {436 board.copyTo(this);437 return true;438 }439 }440 return false;441 };442 Board.prototype.toString = function () {443 var text = "";444 for (var row = 0; row < BoardSize; row++)445 for (var col = 0; col < BoardSize; col++) {446 var val = this._digits[row][col].getValue();447 text += val == 0 ? "." : String(val);448 }449 return text;450 };451 Board.prototype.setString = function (value) {452 // Assumes all input is digits 1..9 or ./space453 if (value.length != (BoardSize * BoardSize))454 return false; //Input string is not of length 81455 var n = 0;456 for (var row = 0; row < BoardSize; row++)457 for (var col = 0; col < BoardSize; col++) {458 var ch = parseInt(value.charAt(n++)); // converts '0' to 0 etc459 var cell = this._digits[row][col];460 cell.setGiven(!isNaN(ch) ? ch : 0);461 }462 this.updateAllowed();463 return true;...
Board.js
Source:Board.js
1function Board() {2 function MultiDimArray(rows, cols) {3 var a = new Array(rows);4 for (var i = 0; i < rows; i++) {5 a[i] = new Array(cols);6 for (var j = 0; j < cols; j++)7 a[i][j] = new Cell();8 }9 return a;10 }11 this._digits = MultiDimArray(BoardSize, BoardSize);12 this._isSolved = false;13 this._isValid = false;14}15Board.prototype.clone = function () {16 var clone = new Board();17 clone._isSolved = this._isSolved;18 clone._isValid = this._isValid;19 clone._digits = new Array(BoardSize);20 for (var i = 0; i < BoardSize; i++) {21 clone._digits[i] = new Array(BoardSize);22 for (var j = 0; j < BoardSize; j++)23 clone._digits[i][j] = this._digits[i][j].clone();24 }25 return clone;26};27Board.prototype.copyTo = function (target) {28 target._isSolved = this._isSolved;29 target._isValid = this._isValid;30 for (var i = 0; i < BoardSize; i++)31 for (var j = 0; j < BoardSize; j++)32 target._digits[i][j] = this._digits[i][j].clone();33};34Board.prototype.getCell = function (loc) {35 return this._digits[loc.row][loc.col];36};37Board.prototype.setCell = function (loc, value) {38 this._digits[loc.row][loc.col] = value;39};40Board.prototype.clear = function () {41 for (var i = 0; i < BoardSize; i++)42 for (var j = 0; j < BoardSize; j++)43 this._digits[i][j].clear();44 this.updateAllowed();45};46Board.prototype.reset = function () {// return Baord to only the givens47 for (var i = 0; i < BoardSize; i++)48 for (var j = 0; j < BoardSize; j++) {49 var cell = this._digits[i][j];50 if (!cell.isGiven())51 cell.clear();52 }53 this.updateAllowed();54};55Board.prototype.checkIsValidSibs = function (loc, digit, locs) {56 for (var i = 0; i < locs.length; i++) {57 var loc = locs[i];58 var cell = this._digits[loc.row][loc.col];59 if (cell.getAnswer() == digit)60 return false;61 }62 return true;63};64Board.prototype.checkIsValid = function (loc, digit) {65 // Checks if the digit can go in that location by checking it doesn't66 // exist in either the row, col or square siblings67 if (!this.checkIsValidSibs(loc, digit, loc.colSibs()))68 return false;69 if (!this.checkIsValidSibs(loc, digit, loc.rowSibs()))70 return false;71 if (!this.checkIsValidSibs(loc, digit, loc.squareSibs()))72 return false;73 return true;74};75Board.prototype.acceptPossibles = function () {76 var more = false;77 var locs = Location.grid();78 for (var i = 0; i < locs.length; i++) {79 var loc = locs[i];80 var cell = this._digits[loc.row][loc.col];81 if (!cell.isAssigned() && cell.hasAnswer() && this.checkIsValid(loc, cell.getAnswer())) {82 cell.setValue(cell.getAnswer()); // if unassigned and has the answer then assign the answer83 more = true;84 }85 }86 return more;87};88Board.prototype.checkForHiddenSingles = function (loc, st) {89 // Check each cell - if not assigned and has no answer then check its siblings90 // get all its allowed then remove all the allowed91 var cell = this.getCell(loc);92 if (!cell.isAssigned() && !cell.hasAnswer()) {93 var allowed = cell.getAllowedClone(); // copy of bit mask of allowed values for this cell94 var locs = loc.getSibs(st);95 for (var i = 0; i < locs.length; i++) {96 var sib = locs[i];97 var sibCell = this.getCell(sib);98 if (!sibCell.isAssigned())99 allowed.removeValues(sibCell.getAllowedClone()); // remove allowed values from siblings100 }101 var answer = allowed.getSingle(); // if there is only one allowed value left (i.e. this cell is the only one amonsgt its sibs with this allowed value)102 // then apply it as the answer. Note getSingle will return 0 (i.e. no anser) if the number of allowed values is not exactly one103 if (answer != 0) {104 cell.setAnswer(answer);105 return true; // no need to check others sibling collections106 }107 }108 return false;109};110Board.prototype.findCellWithFewestChoices = function () {111 var minLocation = Location.empty;112 var minCount = 9;113 var locs = Location.grid();114 for (var i = 0; i < locs.length; i++) {115 var loc = locs[i];116 var cell = this.getCell(loc);117 if (!cell.isAssigned()) {118 var count = cell.getAllowedClone().count();119 if (count < minCount) {120 minLocation = loc;121 minCount = count;122 }123 }124 }125 return minLocation;126};127Board.prototype.updateAllowed = function () {128 // Called whenever the user sets a value or via auto solve129 // Updates the allowed values for each cell based on existing digits130 // entered in a cell's row, col or square131 var cols = new Array(BoardSize);132 var rows = new Array(BoardSize);133 var squares = new Array(BoardSize);134 // First aggregate assigned values to rows, cols, squares135 var locs = Location.grid();136 for (var i = 0; i < locs.length; i++) {137 var loc = locs[i];138 // Disallow for all cells in this row139 var contains = this.getCell(loc).valueMask();140 rows[loc.row] |= contains;141 cols[loc.col] |= contains;142 squares[loc.getSquare()] |= contains;143 }144 // For each cell, aggregate the values already set in that row, col and square.145 // Since the aggregate is a bitmask, the bitwise inverse of that is therefore the allowed values.146 this._isValid = true;147 this._isSolved = true;148 for (var i = 0; i < locs.length; i++) {149 var loc = locs[i];150 // Set allowed values151 var contains = rows[loc.row] | cols[loc.col] | squares[loc.getSquare()];152 var cell = this.getCell(loc);153 cell.setAllowed(~contains); // set allowed values to what values are not already set in this row, col or square154 cell.setAnswer(0); //clear any previous answers155 // As an extra step look for "naked singles", i.e. cells that have only one allowed value, and use156 // that to set the answer (note this is different from the "value" as this can only be assigned157 // by the user or any auto solve functions like "accept singles"158 if (!cell.isAssigned()) {159 this._isSolved = false;160 var mask = new Candidates(~contains);161 var count = mask.count();162 if (count == 0)163 this._isValid = false;164 else if (count == 1)165 cell.setAnswer(mask.getSingle());166 }167 }168 // Step 2: Look for "hidden singles".169 // For each row, col, square, count number of times each digit appears.170 // If any appear once then set that as the answer for that cell.171 // Count in rows172 for (var i = 0; i < locs.length; i++) {173 var loc = locs[i];174 if (!this.checkForHiddenSingles(loc, SibType.Row))// first check row sibs for a hiddne single175 if (!this.checkForHiddenSingles(loc, SibType.Col))// then check cols176 this.checkForHiddenSingles(loc, SibType.Square); // then check square177 }178 // TO DO: Add code here to detect naked/hidden doubles/triples/quads179};180Board.prototype.trySolve = function (loc, value) {// empty Location allowed181 if (!loc.isEmpty())// assign a value to a location if provided182 {183 var cell = this.getCell(loc);184 if (!cell.isAllowed(value))185 throw "Internal error.";186 cell.setValue(value);187 }188 do {189 this.updateAllowed();190 if (!this._isValid)191 return false;192 } while (this.acceptPossibles()); // keep doing deterministic answers193 if (this._isSolved)194 return true;195 if (!this._isValid)196 return false;197 // No deterministic solutions, find cell with the fewest choices and try each one in turn198 // until success.199 var locChoice = this.findCellWithFewestChoices();200 if (locChoice.isEmpty())201 return false;202 var cell = this.getCell(locChoice);203 var Candidates = cell._candidates.CandidatesArray();204 for (var i = 0; i < Candidates.length; i++) {205 var val = Candidates[i];206 var board = this.clone();207 if (board.trySolve(locChoice, val)) {208 board.copyTo(this);209 return true;210 }211 }212 return false;213};214Board.prototype.toString = function () {215 var text = "";216 for (var row = 0; row < BoardSize; row++)217 for (var col = 0; col < BoardSize; col++) {218 var val = this._digits[row][col].getValue();219 text += val == 0 ? "." : String(val);220 }221 return text;222};223Board.prototype.setString = function (value) {224 // Assumes all input is digits 1..9 or ./space225 if (value.length != (BoardSize * BoardSize))226 return false; //Input string is not of length 81227 var n = 0;228 for (var row = 0; row < BoardSize; row++)229 for (var col = 0; col < BoardSize; col++) {230 var ch = parseInt(value.charAt(n++)); // converts '0' to 0 etc231 var cell = this._digits[row][col];232 cell.setGiven(!isNaN(ch) ? ch : 0);233 }234 this.updateAllowed();235 return true;...
MainContainer.js
Source:MainContainer.js
...100 tempHistory.push(gameArray.slice());101 setHistory(tempHistory);102 tempArray[index] = value;103 setGameArray(tempArray);104 console.log("_isSolved(index, value)", _isSolved(index, value));105 if (_isSolved(index, value)) {106 setWon(true);107 }108 }109 }110 function onClickCell(indexOfArray) {111 if (numberSelected !== "0") {112 _userFillCell(indexOfArray, numberSelected);113 }114 setCellSelected(indexOfArray);115 }116 function _userFillCell(index, value) {117 if (value === solvedArray[index]) {118 triggerCorrectAnswer(index, value);119 // _fillCell(index, value);120 } else {121 triggerWrongAnswer(index, value);122 }123 }124 async function triggerCorrectAnswer(index, value) {125 addPoint();126 setTimeTurnStarted();127 setColorFlash("green");128 _fillCell(index, value);129 await setTimeout(() => finishCorrectAnswer(), 1000);130 }131 // clear flash color and refill cell132 function finishCorrectAnswer(index, value) {133 setColorFlash(null);134 _fillCell(index, value);135 setCellSelected(-1);136 }137 async function triggerWrongAnswer(index, value) {138 setColorFlash("red");139 _fillCell(index, value);140 await setTimeout(() => clearWrongAnswer(), 2000);141 }142 // clear flash color, clear selected cell, erase cell value143 function clearWrongAnswer() {144 setColorFlash(null);145 onClickErase();146 setCellSelected(-1);147 nextPlayer();148 setTimeTurnStarted();149 }150 function _isSolved(index, value) {151 if (152 gameArray.every((cell, cellIndex) => {153 if (cellIndex === index) {154 return value === solvedArray[cellIndex];155 } else {156 return cell === solvedArray[cellIndex];157 }158 })159 ) {160 return true;161 }162 return false;163 }164 function onClickErase() {...
CardsScene.js
Source:CardsScene.js
1import Phaser from 'phaser';2import { CONFIG } from '../../config/index.js';34class Card {5 constructor(scene, info) {6 this._scene = scene;7 this._key = info.key;8 this._currentSprite = this._createSprite(info.x, info.y, 'cardBack');9 this._state = 'down';10 this._isDragging = false;11 this.callback = info.callback;12 }1314 toggleState() {15 const x = this.x;16 const y = this.y;17 this._currentSprite.destroy();1819 if (this._state === 'down') {20 this._currentSprite = this._createSprite(x, y, this._key);21 this._state = 'up';22 } else {23 this._currentSprite = this._createSprite(x, y, 'cardBack');24 this._state = 'down';25 }26 }2728 get x() {29 return this._currentSprite.x;30 }3132 get y() {33 return this._currentSprite.y;34 }3536 _createSprite(x, y, key) {37 const sprite = this._scene.add.image(x, y, key);38 sprite.setInteractive();39 this._scene.input.setDraggable(sprite);40 sprite.on('pointerup', () => {41 if (!this._lastTime) {42 this._lastTime = this._scene.time.now;43 return;44 }4546 let clickDelay = this._scene.time.now - this._lastTime;47 if (clickDelay < 350) {48 this.toggleState();49 this.callback(this._key, this._state);50 }5152 this._lastTime = this._scene.time.now;53 });54 sprite.on('drag', (pointer, x, y) => {55 sprite.x = x;56 sprite.y = y;57 });5859 return sprite;60 }61}6263export default class CardsScene extends Phaser.Scene {64 constructor() {65 super({ key: 'CardsScene' });66 }6768 init() {69 this._isSolved = false;70 }7172 preload() {73 CardsScene.ALL_CARDS.forEach((card) => {74 this.load.image(card, `/find/sprites/cards/${card}.png`);75 });7677 this.load.image('cardBack', '/find/sprites/cards/back.png');78 this.load.audio(CONFIG.sound.airHorn.key, CONFIG.sound.airHorn.location);79 }8081 create() {82 this.cameras.main.setBackgroundColor('#000000');83 this.scene.get('GameScene').events.emit('disable_input');8485 this._upCards = [];86 CardsScene.ALL_CARDS.forEach((card) => {87 const x = Math.floor(Math.random() * 350 + 50);88 const y = Math.floor(Math.random() * 350 + 50);89 new Card(this, {90 key: card,91 x,92 y,93 callback: (key, state) => {94 if (state === 'up') {95 this._upCards.push(key);96 } else {97 this._upCards = this._upCards.filter(a => a !== key);98 }99100 this._checkSolution();101 if (this._isSolved) {102 this.winText.visible = true;103 this.sound.play(CONFIG.sound.airHorn.key);104 }105 }106 });107 });108109 this.winText = this.add.text(20, 20, 'WINNER!!!!111111111111111111', {110 fontSize: 60,111 });112113 this.winText.visible = false;114 this.winTween = this.tweens.add({115 targets: [this.winText],116 props: {117 angle: {118 duration: 500,119 from: 0,120 to: 359,121 repeat: -1,122 },123 x: {124 from: 0,125 to: 500,126 duration: 2000,127 repeat: -1,128 },129 y: {130 from: 300,131 to: 0,132 duration: 3102,133 repeat: -1,134 },135 },136 });137138 this.leaveText = this.add.text(500, 20, 'LEAVE', {139 color: '#CCCCFF',140 fontSize: 20,141 });142 this.leaveText.setInteractive()143 .on('pointerup', () => {144 this.scene.stop();145 this.scene.get('GameScene').events.emit('enable_input');146 this.scene.resume('GameScene');147 })148 ;149 }150151 update() {152153 }154155 _checkSolution() {156 if (this._upCards.filter((cardName) => cardName.match(/[a-z]1$/)).length < 1) {157 return;158 }159160 if (this._upCards.filter((cardName) => cardName.match(/[a-z]3$/)).length < 2) {161 return;162 }163 164 if (this._upCards.filter((cardName) => cardName.match(/[a-z]7$/)).length < 1) {165 return;166 }167168 this._isSolved = true;169 }170}171172CardsScene.ALL_CARDS = [];173174['clubs', 'diamond', 'hearts', 'spades'].forEach((suit) => {175 for (let cardinalValue = 1; cardinalValue <= 13; cardinalValue++) {176 const key = `${suit}${cardinalValue}`;177 CardsScene.ALL_CARDS.push(key);178 }
...
Using AI Code Generation
1describe('Test', () => {2 it('test', () => {3 cy.get('.square').each(($el, index, $list) => {4 cy.wrap($el).click()5 })6 })7})8describe('Test', () => {9 it('test', () => {10 cy.get('.square').each(($el, index, $list) => {11 cy.wrap($el).click()12 })13 })14})15describe('Test', () => {16 it('test', () => {17 cy.get('.square').each(($el, index, $list) => {18 cy.wrap($el).click()19 })20 })21})22describe('Test', () => {23 it('test', () => {24 cy.get('.square').each(($el, index, $list) => {25 cy.wrap($el).click()26 })27 })28})29describe('Test', () => {30 it('test', () => {31 cy.get('.square').each(($el, index, $list) => {32 cy.wrap($el).click()33 })34 })35})36describe('Test', () => {37 it('test', () => {38 cy.get('.square').each(($el, index, $list) => {39 cy.wrap($el).click()40 })41 })42})43describe('Test', () => {44 it('test', () => {45 cy.get('.square').each(($el, index, $list) => {46 cy.wrap($el).click()47 })48 })49})50describe('Test', () => {51 it('test', () => {
Using AI Code Generation
1var sudoku = new Cypress.Sudoku();2sudoku._isSolved();3sudoku._isSolved(0, 0, 1);4sudoku._isSolved(0, 0, 2);5sudoku._isSolved(0, 0, 3);6sudoku._isSolved(0, 0, 4);7sudoku._isSolved(0, 0, 5);8sudoku._isSolved(0, 0, 6);9sudoku._isSolved(0, 0, 7);10sudoku._isSolved(0, 0, 8);11sudoku._isSolved(0, 0, 9);12var sudoku = new Cypress.Sudoku();13sudoku._getRandomNumber();14sudoku._getRandomNumber(1);15sudoku._getRandomNumber(2);16sudoku._getRandomNumber(3);17sudoku._getRandomNumber(4);18sudoku._getRandomNumber(5);19sudoku._getRandomNumber(6);20sudoku._getRandomNumber(7);21sudoku._getRandomNumber(8);22sudoku._getRandomNumber(9);23var sudoku = new Cypress.Sudoku();24sudoku._getRandomEmptyCell();25var sudoku = new Cypress.Sudoku();26sudoku._getRandomEmptyCell({row: 0, col: 0});27var sudoku = new Cypress.Sudoku();28sudoku._getRandomEmptyCell({row: 0, col: 0}, 1);
Using AI Code Generation
1function _isSolved() {2 return cy.window().then((win) => {3 const game = win.game;4 return game._isSolved();5 });6}7it('should be solved', () => {8 _isSolved().should('be.true');9});10describe('Sudoku', () => {11 it('should be solved', () => {12 cy.visit('index.html');13 cy.get('#solve-button').click();14 cy.window().then((win) => {15 const game = win.game;16 expect(game._isSolved()).to.be.true;17 });18 });19});20describe('Sudoku', () => {21 it('should be solved', () => {22 cy.visit('index.html');23 cy.get('#solve-button').click();24 cy.window().then((win) => {25 const game = win.game;26 expect(game._isSolved()).to.be.true;27 });28 });29});30describe('Sudoku', () => {31 it('should be solved', () => {32 cy.visit('index.html');33 cy.get('#solve-button').click();34 cy.window().then((win) => {35 const game = win.game;36 expect(game._isSolved()).to.be.true;37 });38 });39});40describe('Sudoku', () => {41 it('should be solved', () => {42 cy.visit('index.html');43 cy.get('#solve-button').click();44 cy.window().then((win) => {45 const game = win.game;46 expect(game._isSolved()).to.be.true;47 });48 });49});50describe('Sudoku', () => {51 it('should be solved', () => {52 cy.visit('index.html');53 cy.get('#solve-button').click();54 cy.window().then((win) => {55 const game = win.game;56 expect(game
Using AI Code Generation
1cy.get("div").then(($div) => {2 const isSolved = $div[0]._isSolved();3 expect(isSolved).to.be.true;4});5Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {6 const isSolved = $div[0]._isSolved();7 expect(isSolved).to.be.true;8});9cy.get("div").isSolved();10Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {11 const isSolved = $div[0]._isSolved();12 expect(isSolved).to.be.true;13});14cy.get("div").isSolved();15Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {16 const isSolved = $div[0]._isSolved();17 expect(isSolved).to.be.true;18});19cy.get("div").isSolved();20Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {21 const isSolved = $div[0]._isSolved();22 expect(isSolved).to.be.true;23});24cy.get("div").isSolved();25Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {26 const isSolved = $div[0]._isSolved();27 expect(isSolved).to.be.true;28});29cy.get("div").isSolved();30Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {31 const isSolved = $div[0]._isSolved();32 expect(isSolved).to.be.true;33});34cy.get("div").isSolved();35Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {36 const isSolved = $div[0]._isSolved();37 expect(isSolved).to.be.true;38});39cy.get("div").isSolved();40Cypress.Commands.add("isSolved", { prevSubject: true }, ($div) => {41 const isSolved = $div[0]._isSolved();42 expect(is
Using AI Code Generation
1cy.get('div[data-cy=sudoku]').then((element) => {2 let sudoku = new Sudoku(element[0].innerText);3 expect(sudoku._isSolved()).to.be.true;4});5class Sudoku {6 constructor(string) {7 this._string = string;8 }9 _isSolved() {10 return this._string.includes("0") ? false : true;11 }12}13You can’t access the innerText of the element because it’s not a DOM element. You can only access the DOM of the page you’re testing, not the DOM of the application you’re testing. You can access the DOM of the application by using cy.window() , but you can’t access the innerText of the element you’re testing because it’s not
Using AI Code Generation
1Cypress._.memoize.cache = new Map();2const _isSolved = Cypress._.memoize((sudoku) => {3});4Cypress.Commands.add('solveSudoku', (sudoku) => {5});6Cypress.Commands.add('solveSudoku', (sudoku) => {7});
Using AI Code Generation
1describe('Test', () => {2 it('should be able to run', () => {3 cy.contains('type').click();4 cy.url().should('include', '/commands/actions');5 });6});7Cypress._.memoize.Cache.prototype._isSolved = function () {8 return true;9};10module.exports = (on, config) => {11 on('task', {12 isSolved() {13 return true;14 },15 });16};
Cypress is a renowned Javascript-based open-source, easy-to-use end-to-end testing framework primarily used for testing web applications. Cypress is a relatively new player in the automation testing space and has been gaining much traction lately, as evidenced by the number of Forks (2.7K) and Stars (42.1K) for the project. LambdaTest’s Cypress Tutorial covers step-by-step guides that will help you learn from the basics till you run automation tests on LambdaTest.
You can elevate your expertise with end-to-end testing using the Cypress automation framework and stay one step ahead in your career by earning a Cypress certification. Check out our Cypress 101 Certification.
Watch this 3 hours of complete tutorial to learn the basics of Cypress and various Cypress commands with the Cypress testing at LambdaTest.
Get 100 minutes of automation test minutes FREE!!