|
| 1 | +//42 - TORNEO DRAGON BALL |
| 2 | +/* |
| 3 | + * EJERCICIO: |
| 4 | + * ¡El último videojuego de Dragon Ball ya está aquí! |
| 5 | + * Se llama Dragon Ball: Sparking! ZERO. |
| 6 | + * |
| 7 | + * Simula un Torneo de Artes Marciales, al más puro estilo |
| 8 | + * de la saga, donde participarán diferentes luchadores, y el |
| 9 | + * sistema decidirá quién es el ganador. |
| 10 | + * |
| 11 | + * Luchadores: |
| 12 | + * - Nombre. |
| 13 | + * - Tres atributos: velocidad, ataque y defensa |
| 14 | + * (con valores entre 0 a 100 que tú decidirás). |
| 15 | + * - Comienza cada batalla con 100 de salud. |
| 16 | + * Batalla: |
| 17 | + * - En cada batalla se enfrentan 2 luchadores. |
| 18 | + * - El luchador con más velocidad comienza atacando. |
| 19 | + * - El daño se calcula restando el daño de ataque del |
| 20 | + * atacante menos la defensa del oponente. |
| 21 | + * - El oponente siempre tiene un 20% de posibilidad de |
| 22 | + * esquivar el ataque. |
| 23 | + * - Si la defensa es mayor que el ataque, recibe un 10% |
| 24 | + * del daño de ataque. |
| 25 | + * - Después de cada turno y ataque, el oponente pierde salud. |
| 26 | + * - La batalla finaliza cuando un luchador pierde toda su salud. |
| 27 | + * Torneo: |
| 28 | + * - Un torneo sólo es válido con un número de luchadores |
| 29 | + * potencia de 2. |
| 30 | + * - El torneo debe crear parejas al azar en cada ronda. |
| 31 | + * - Los luchadores se enfrentan en rondas eliminatorias. |
| 32 | + * - El ganador avanza a la siguiente ronda hasta que sólo |
| 33 | + * quede uno. |
| 34 | + * - Debes mostrar por consola todo lo que sucede en el torneo, |
| 35 | + * así como el ganador. |
| 36 | + */ |
| 37 | + |
| 38 | +//We use https://sigmawire.net/image-to-url-converter service to include image in our javascript code |
| 39 | + |
| 40 | +class Fighter { |
| 41 | + constructor(id, name, speed, attack, defense) { |
| 42 | + this.id = id; |
| 43 | + this.name = name; |
| 44 | + this.speed = speed; |
| 45 | + this.attack = attack; |
| 46 | + this.defense = defense; |
| 47 | + this.health = 100; // Each fighter starts with 100 health |
| 48 | + } |
| 49 | + |
| 50 | + calculateDamage(opponent) { |
| 51 | + let damage = Math.max(this.attack - opponent.defense, 0); // No negative damage |
| 52 | + |
| 53 | + // Check if the opponent dodges the attack |
| 54 | + if (Math.random() < 0.2) { // 20% chance to dodge |
| 55 | + return 0; // No damage dealt |
| 56 | + } |
| 57 | + |
| 58 | + // If the opponent's defense is greater than the attack, reduce damage |
| 59 | + if (opponent.defense > this.attack) { |
| 60 | + damage *= 0.1; // 10% of the attack damage |
| 61 | + } |
| 62 | + |
| 63 | + return Math.floor(damage); // Return the damage as an integer |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +class Battle { |
| 69 | + constructor(fighter1, fighter2) { |
| 70 | + this.fighter1 = fighter1; |
| 71 | + this.fighter2 = fighter2; |
| 72 | + this.attacker = fighter1.speed >= fighter2.speed ? fighter1 : fighter2; |
| 73 | + this.defender = this.attacker === fighter1 ? fighter2 : fighter1; |
| 74 | + } |
| 75 | + winner(winFighter) { |
| 76 | + let fighter = winFighter; |
| 77 | + let element = document.querySelectorAll('.contrincant')[fighter.id - 1]; |
| 78 | + |
| 79 | + if (element.classList.contains('loser')) { |
| 80 | + element.classList.remove('loser'); |
| 81 | + } |
| 82 | + |
| 83 | + element.classList.add('winner'); |
| 84 | + } |
| 85 | + |
| 86 | + |
| 87 | + loser(losFighter) { |
| 88 | + let fighter = losFighter; |
| 89 | + let element = document.querySelectorAll('.contrincant')[fighter.id - 1]; |
| 90 | + |
| 91 | + if (element.classList.contains('winner')) { |
| 92 | + element.classList.remove('winner'); |
| 93 | + } |
| 94 | + |
| 95 | + element.classList.add('loser'); |
| 96 | + } |
| 97 | + |
| 98 | + async start() { |
| 99 | + while (this.fighter1.health > 0 && this.fighter2.health > 0) { |
| 100 | + const damage = this.attacker.calculateDamage(this.defender); |
| 101 | + |
| 102 | + // Ensure a minimum level of damage |
| 103 | + const minDamage = 10; |
| 104 | + const actualDamage = Math.max(minDamage, damage); |
| 105 | + |
| 106 | + this.defender.health -= actualDamage; |
| 107 | + |
| 108 | + // Log the attack and damage |
| 109 | + if (actualDamage === minDamage) { |
| 110 | + logMessage(`${this.attacker.name}'s attack was partially blocked by ${this.defender.name}, dealing ${actualDamage} damage.`); |
| 111 | + } else { |
| 112 | + logMessage(`${this.attacker.name} attacks ${this.defender.name} for ${actualDamage} damage.`); |
| 113 | + } |
| 114 | + logMessage(`${this.defender.name} has ${this.defender.health} health left.`); |
| 115 | + |
| 116 | + // Swap roles for the next turn |
| 117 | + [this.attacker, this.defender] = [this.defender, this.attacker]; |
| 118 | + |
| 119 | + // Introduce a delay between attacks |
| 120 | + await new Promise(resolve => setTimeout(resolve, 500)); |
| 121 | + } |
| 122 | + |
| 123 | + const winner = this.fighter1.health > 0 ? this.fighter1 : this.fighter2; |
| 124 | + logMessage(`${winner.name} wins the battle!`); |
| 125 | + const winningFighter = this.fighter1.health > 0 ? this.fighter1 : this.fighter2; |
| 126 | + const losingFighter = winningFighter === this.fighter1 ? this.fighter2 : this.fighter1; |
| 127 | + |
| 128 | + // Add Winner and Loser class to fighter containers respectibily |
| 129 | + this.winner(winningFighter); |
| 130 | + this.loser(losingFighter); |
| 131 | + |
| 132 | + return winner; |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +class Tournament { |
| 137 | + constructor(fighters) { |
| 138 | + if (!this.isPowerOfTwo(fighters.length)) { |
| 139 | + throw new Error("Number of fighters must be a power of 2."); |
| 140 | + } |
| 141 | + this.fighters = fighters; |
| 142 | + } |
| 143 | + |
| 144 | + isPowerOfTwo(n) { |
| 145 | + return (n & (n - 1)) === 0 && n > 0; // Check if n is a power of two |
| 146 | + } |
| 147 | + |
| 148 | + async start() { |
| 149 | + logMessage("Starting the tournament..."); |
| 150 | + let round = 1; |
| 151 | + |
| 152 | + while (this.fighters.length > 1) { |
| 153 | + logMessage(`\n--- Round ${round} ---`); |
| 154 | + |
| 155 | + const winners = []; |
| 156 | + for (let i = 0; i < this.fighters.length; i += 2) { |
| 157 | + const battle = new Battle(this.fighters[i], this.fighters[i + 1]); |
| 158 | + const winner = await battle.start(); |
| 159 | + winners.push(winner); |
| 160 | + } |
| 161 | + |
| 162 | + logMessage("\nWinners of Round " + round + ":"); |
| 163 | + winners.forEach(fighter => { |
| 164 | + logMessage("- " + fighter.name); |
| 165 | + }); |
| 166 | + |
| 167 | + this.fighters = winners; // Update fighters for the next round |
| 168 | + round++; |
| 169 | + } |
| 170 | + |
| 171 | + logMessage(`\nThe champion of the tournament is ${this.fighters[0].name}!`); |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | + |
| 176 | +const fighters = [ |
| 177 | + new Fighter(1, 'Goku', 95, 80, 50), |
| 178 | + new Fighter(2, 'Vegeta', 90, 85, 45), |
| 179 | + new Fighter(3, 'Gohan', 85, 75, 55), |
| 180 | + new Fighter(4, 'Piccolo', 80, 70, 60), |
| 181 | + new Fighter(5, 'Frieza', 88, 90, 40), |
| 182 | + new Fighter(6, 'Cell', 85, 80, 50), |
| 183 | + new Fighter(7, 'Majin Buu', 70, 60, 70), |
| 184 | + new Fighter(8, 'Trunks', 75, 65, 65) |
| 185 | +]; |
| 186 | + |
| 187 | +// DOM Manipulation |
| 188 | +const styles = ` |
| 189 | + body { |
| 190 | + background: #000; |
| 191 | + display: flex; |
| 192 | + justify-content: center; |
| 193 | + align-items: center; |
| 194 | + transition: background .5s ease; |
| 195 | + margin: 0; |
| 196 | + padding: 0; |
| 197 | + } |
| 198 | +
|
| 199 | + .winner{ |
| 200 | + display: flex; |
| 201 | + justify-content: center; |
| 202 | + align-items: center; |
| 203 | + opacity: 1; |
| 204 | + transform: scale(1.1); |
| 205 | + font-weight: 600; |
| 206 | + } |
| 207 | + .winner::before{ |
| 208 | + content: 'WINNER'; |
| 209 | + color: rgba(0,255,0,0.4); |
| 210 | + } |
| 211 | +
|
| 212 | + .loser{ |
| 213 | + display: flex; |
| 214 | + justify-content: center; |
| 215 | + align-items: center; |
| 216 | + opacity: 0.5; |
| 217 | + font-weight: 600; |
| 218 | + } |
| 219 | +
|
| 220 | + .loser::after{ |
| 221 | + content: 'LOSER'; |
| 222 | + color: rgba(255,0,0,0.7); |
| 223 | + } |
| 224 | + |
| 225 | + .contrincants_wrapper { |
| 226 | + margin-top: 20px; |
| 227 | + display: flex; |
| 228 | + justify-content: center; |
| 229 | + align-items: center; |
| 230 | + flex-flow: row wrap; |
| 231 | + width: 95vw; |
| 232 | + gap: 10px; |
| 233 | + } |
| 234 | +
|
| 235 | + .contrincant { |
| 236 | + width: 200px; |
| 237 | + height: 200px; |
| 238 | + border: 1px solid #fff outset; |
| 239 | + object-fit: cover; |
| 240 | + object-position: center center; |
| 241 | + flex: 0 0 70px; |
| 242 | + background-size: cover; |
| 243 | + background-position: center; |
| 244 | + } |
| 245 | +
|
| 246 | + .log_container { |
| 247 | + background: rgba(0, 0, 0, 0.7); |
| 248 | + padding: 10px; |
| 249 | + border-radius: 5px; |
| 250 | + max-height: 200px; |
| 251 | + overflow-y: auto; |
| 252 | + z-index: 1; |
| 253 | + border: 1px solid #fff; |
| 254 | + color: #fff; |
| 255 | + margin-top: 20px; |
| 256 | + width: 100%; |
| 257 | + text-align: center; |
| 258 | + } |
| 259 | +`; |
| 260 | + |
| 261 | +// Create a <style> element and append the styles |
| 262 | +let styleSheet = document.createElement("style"); |
| 263 | +styleSheet.type = "text/css"; |
| 264 | +styleSheet.innerText = styles; |
| 265 | +document.head.appendChild(styleSheet); |
| 266 | + |
| 267 | +// Create the combatants' wrapper |
| 268 | +let div_wrapper = document.createElement('div'); |
| 269 | +div_wrapper.classList.add('contrincants_wrapper'); |
| 270 | + |
| 271 | +// Create a DocumentFragment to batch updates |
| 272 | +let fragment = document.createDocumentFragment(); |
| 273 | + |
| 274 | +// Create and append combatants to the fragment |
| 275 | +for (let i = 0; i < fighters.length; i++) { |
| 276 | + let div = document.createElement('div'); |
| 277 | + div.classList.add('contrincant'); |
| 278 | + div.style.backgroundImage = `url(https://i.imgur.com/${['vDUO50X', 'fDitnqa', 'XHhWd32', 'yea6HcH', 'BTaQ8g4', 'LACYE3s', 'QIUnWuQ', 'SHXxvjC'][i]}.jpeg)`; |
| 279 | + fragment.appendChild(div); |
| 280 | +} |
| 281 | + |
| 282 | +// Append the fragment to the wrapper |
| 283 | +div_wrapper.appendChild(fragment); |
| 284 | + |
| 285 | +// Create the log container |
| 286 | +let logContainer = document.createElement('div'); |
| 287 | +logContainer.classList.add('log_container'); |
| 288 | +div_wrapper.appendChild(logContainer); |
| 289 | + |
| 290 | +// Append the wrapper to the body |
| 291 | +document.body.appendChild(div_wrapper); |
| 292 | + |
| 293 | +// Function to log messages to the log container |
| 294 | +function logMessage(message) { |
| 295 | + const logEntry = document.createElement('div'); |
| 296 | + logEntry.textContent = message; |
| 297 | + logContainer.appendChild(logEntry); |
| 298 | + logContainer.scrollTop = logContainer.scrollHeight; // Auto-scroll to the bottom |
| 299 | +} |
| 300 | + |
| 301 | +// Override console.log to also log to the log container |
| 302 | +console.log = (function(originalLog) { |
| 303 | + return function(...args) { |
| 304 | + originalLog.apply(console, args); // Call the original console.log |
| 305 | + logMessage(args.join(' ')); // Log to the log container |
| 306 | + }; |
| 307 | +})(console.log); |
| 308 | + |
| 309 | +// Start the tournament |
| 310 | +const tournament = new Tournament(fighters); |
| 311 | +tournament.start(); |
0 commit comments