From a1d22369cb334634d17f3affd2c74e4d417aeb0c Mon Sep 17 00:00:00 2001 From: JiWoong Sul Date: Mon, 12 Jan 2026 20:02:45 +0900 Subject: [PATCH] =?UTF-8?q?feat(engine):=20=EC=97=94=EC=A7=84=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BA=90=EB=A6=AD=ED=84=B0=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProgressService 로직 개선 - RewardService 확장 - CombatCalculator, ItemService 정리 - TestCharacterService 추가 --- lib/src/core/engine/combat_calculator.dart | 4 +- lib/src/core/engine/item_service.dart | 30 +- lib/src/core/engine/progress_service.dart | 125 +++++--- lib/src/core/engine/reward_service.dart | 77 ++++- .../core/engine/test_character_service.dart | 268 ++++++++++++++++++ 5 files changed, 434 insertions(+), 70 deletions(-) create mode 100644 lib/src/core/engine/test_character_service.dart diff --git a/lib/src/core/engine/combat_calculator.dart b/lib/src/core/engine/combat_calculator.dart index c8918d1..91f8e28 100644 --- a/lib/src/core/engine/combat_calculator.dart +++ b/lib/src/core/engine/combat_calculator.dart @@ -98,9 +98,9 @@ class CombatCalculator { final isParried = parryRoll < defenderParryRate; // 3. 기본 데미지 계산 (0.8 ~ 1.2 변동) - // DEF 감산 비율: 0.3 (기존 0.5에서 축소하여 의미있는 피해 보장) + // DEF 감산 비율: 0.5 (방어력 효과 상향, 몬스터 ATK 하향과 연동) final damageVariation = 0.8 + rng.nextDouble() * 0.4; - var baseDamage = (attackerAtk * damageVariation - defenderDef * 0.3); + var baseDamage = (attackerAtk * damageVariation - defenderDef * 0.5); // 4. 크리티컬 판정 final criRoll = rng.nextDouble(); diff --git a/lib/src/core/engine/item_service.dart b/lib/src/core/engine/item_service.dart index 7922f97..0756f4f 100644 --- a/lib/src/core/engine/item_service.dart +++ b/lib/src/core/engine/item_service.dart @@ -135,30 +135,36 @@ class ItemService { } /// 방패 스탯 생성 + /// + /// DEF 배율 조정 (v2): 방패 DEF를 0.15배로 축소 ItemStats _generateShieldStats(int baseValue, ItemRarity rarity) { final blockBonus = 0.05 + rarity.index * 0.02; + final def = (baseValue * 0.15).round(); - return ItemStats(def: baseValue ~/ 2, blockRate: blockBonus); + return ItemStats(def: def, blockRate: blockBonus); } /// 방어구 스탯 생성 + /// + /// DEF 배율 조정 (v2): 9개 방어구 합산 DEF ≈ 무기 ATK × 1.6 + /// 기존 배율(합계 8.0)에서 대폭 축소하여 일반 공격 데미지 정상화 ItemStats _generateArmorStats( int baseValue, ItemRarity rarity, EquipmentSlot slot, ) { - // 슬롯별 방어력 가중치 + // 슬롯별 방어력 가중치 (총합 ~1.6으로 축소) final defMultiplier = switch (slot) { - EquipmentSlot.hauberk => 1.5, // 갑옷류 최고 - EquipmentSlot.helm => 1.2, - EquipmentSlot.gambeson => 1.0, - EquipmentSlot.cuisses => 0.9, - EquipmentSlot.greaves => 0.8, - EquipmentSlot.brassairts => 0.7, - EquipmentSlot.vambraces => 0.7, - EquipmentSlot.gauntlets => 0.6, - EquipmentSlot.sollerets => 0.6, - _ => 0.5, + EquipmentSlot.hauberk => 0.30, // 갑옷류 최고 + EquipmentSlot.helm => 0.25, + EquipmentSlot.gambeson => 0.20, + EquipmentSlot.cuisses => 0.18, + EquipmentSlot.greaves => 0.16, + EquipmentSlot.brassairts => 0.14, + EquipmentSlot.vambraces => 0.14, + EquipmentSlot.gauntlets => 0.12, + EquipmentSlot.sollerets => 0.12, + _ => 0.10, }; final def = (baseValue * defMultiplier).round(); diff --git a/lib/src/core/engine/progress_service.dart b/lib/src/core/engine/progress_service.dart index 8d123c4..d98528c 100644 --- a/lib/src/core/engine/progress_service.dart +++ b/lib/src/core/engine/progress_service.dart @@ -6,6 +6,7 @@ import 'package:asciineverdie/src/core/engine/combat_calculator.dart'; import 'package:asciineverdie/src/core/engine/game_mutations.dart'; import 'package:asciineverdie/src/core/engine/potion_service.dart'; import 'package:asciineverdie/src/core/engine/reward_service.dart'; +import 'package:asciineverdie/src/core/engine/shop_service.dart'; import 'package:asciineverdie/src/core/engine/skill_service.dart'; import 'package:asciineverdie/src/core/model/combat_event.dart'; import 'package:asciineverdie/src/core/model/combat_state.dart'; @@ -13,6 +14,7 @@ import 'package:asciineverdie/src/core/model/combat_stats.dart'; import 'package:asciineverdie/src/core/model/equipment_item.dart'; import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/game_state.dart'; +import 'package:asciineverdie/src/core/model/item_stats.dart'; import 'package:asciineverdie/src/core/model/monster_combat_stats.dart'; import 'package:asciineverdie/src/core/model/monster_grade.dart'; import 'package:asciineverdie/src/core/model/potion.dart'; @@ -514,12 +516,15 @@ class ProgressService { return (progress: progress, queue: queue); } - // 2. kill 태스크가 아니었고 heading도 아니면 heading 또는 buying 태스크 실행 - // (원본 670-677줄) - if (oldTaskType != TaskType.kill && oldTaskType != TaskType.neutral) { - // Gold가 충분하면 장비 구매 (원본 671-673줄) + // 2. kill/heading/buying 태스크가 아니었으면 heading 또는 buying 태스크 실행 + // (원본 670-677줄) - buying 완료 후 무한 루프 방지 + if (oldTaskType != TaskType.kill && + oldTaskType != TaskType.neutral && + oldTaskType != TaskType.buying) { + // Gold가 충분하면 장비 구매 (Common 장비 가격 기준) + // 실제 구매 가격과 동일한 공식 사용: level * 50 final gold = _getGold(state); - final equipPrice = _equipPrice(state.traits.level); + final equipPrice = state.traits.level * 50; // Common 장비 1개 가격 if (gold > equipPrice) { final taskResult = pq_logic.startTask( progress, @@ -966,8 +971,12 @@ class ProgressService { final nextLevel = state.traits.level + 1; final rng = state.rng; - final hpGain = state.stats.con ~/ 3 + 1 + rng.nextInt(4); - final mpGain = state.stats.intelligence ~/ 3 + 1 + rng.nextInt(4); + + // HP/MP 증가량 (PlayerScaling 기반 + 랜덤 변동) + // 기존: CON/3 + 1 + random(0-3) → ~6-9 HP/레벨 (너무 낮음) + // 신규: 18 + CON/5 + random(0-4) → ~20-25 HP/레벨 (생존율 개선) + final hpGain = 18 + state.stats.con ~/ 5 + rng.nextInt(5); + final mpGain = 6 + state.stats.intelligence ~/ 5 + rng.nextInt(3); var nextState = state.copyWith( traits: state.traits.copyWith(level: nextLevel), @@ -1078,29 +1087,48 @@ class ProgressService { return state.inventory.gold; } - /// 장비 가격 계산 (원본 Main.pas:612-616) - /// Result := 5 * Level^2 + 10 * Level + 20 - int _equipPrice(int level) { - return 5 * level * level + 10 * level + 20; - } - - /// 장비 구매 완료 처리 (원본 Main.pas:631-634) + /// 장비 구매 완료 처리 (개선된 로직) + /// + /// 1순위: 빈 슬롯에 Common 장비 최대한 채우기 + /// 2순위: 골드 남으면 물약 구매 GameState _completeBuying(GameState state) { + var nextState = state; final level = state.traits.level; - final price = _equipPrice(level); + final shopService = ShopService(rng: nextState.rng); - // Gold 차감 (inventory.gold 필드 사용) - final newGold = math.max(0, state.inventory.gold - price); - var nextState = state.copyWith( - inventory: state.inventory.copyWith(gold: newGold), - ); + // 1. 빈 슬롯 목록 수집 + final emptySlots = []; + for (var i = 0; i < Equipment.slotCount; i++) { + if (nextState.equipment.getItemByIndex(i).isEmpty) { + emptySlots.add(i); + } + } - // 장비 획득 (WinEquip) - // 원본 Main.pas:797 - posn := Random(Equips.Items.Count); (11개 슬롯) - final slotIndex = nextState.rng.nextInt(Equipment.slotCount); - nextState = mutations.winEquipByIndex(nextState, level, slotIndex); + // 2. 골드가 허용하는 한 빈 슬롯에 Common 장비 구매 + for (final slotIndex in emptySlots) { + final slot = EquipmentSlot.values[slotIndex]; + final item = shopService.generateShopItem( + playerLevel: level, + slot: slot, + targetRarity: ItemRarity.common, + ); + final price = shopService.calculateBuyPrice(item); - // 물약 자동 구매 (남은 골드의 20% 사용) + if (nextState.inventory.gold >= price) { + nextState = nextState.copyWith( + inventory: nextState.inventory.copyWith( + gold: nextState.inventory.gold - price, + ), + equipment: nextState.equipment + .setItemByIndex(slotIndex, item) + .copyWith(bestIndex: slotIndex), + ); + } else { + break; // 골드 부족 시 중단 + } + } + + // 3. 물약 자동 구매 (남은 골드의 20% 사용) final potionService = const PotionService(); final purchaseResult = potionService.autoPurchasePotions( playerLevel: level, @@ -1644,30 +1672,35 @@ class ProgressService { required String killerName, required DeathCause cause, }) { - // 상실할 장비 개수 계산 - final lostCount = state.equipment.equippedItems.length; - // 사망 직전 전투 이벤트 저장 (최대 10개) final lastCombatEvents = state.progress.currentCombat?.recentEvents ?? const []; - // 빈 장비 생성 (기본 무기만 유지) - final emptyEquipment = Equipment( - items: [ - EquipmentItem.defaultWeapon(), - EquipmentItem.empty(EquipmentSlot.shield), - EquipmentItem.empty(EquipmentSlot.helm), - EquipmentItem.empty(EquipmentSlot.hauberk), - EquipmentItem.empty(EquipmentSlot.brassairts), - EquipmentItem.empty(EquipmentSlot.vambraces), - EquipmentItem.empty(EquipmentSlot.gauntlets), - EquipmentItem.empty(EquipmentSlot.gambeson), - EquipmentItem.empty(EquipmentSlot.cuisses), - EquipmentItem.empty(EquipmentSlot.greaves), - EquipmentItem.empty(EquipmentSlot.sollerets), - ], - bestIndex: 0, - ); + // 무기(슬롯 0)를 제외한 장착된 장비 중 1개를 제물로 삭제 + // 장착된 비무기 슬롯 인덱스 수집 (슬롯 1~10 중 장비가 있는 것) + final equippedNonWeaponSlots = []; + for (var i = 1; i < Equipment.slotCount; i++) { + if (state.equipment.getItemByIndex(i).isNotEmpty) { + equippedNonWeaponSlots.add(i); + } + } + + // 제물로 바칠 장비 선택 및 삭제 + var newEquipment = state.equipment; + final lostCount = equippedNonWeaponSlots.isNotEmpty ? 1 : 0; + + if (equippedNonWeaponSlots.isNotEmpty) { + // 랜덤하게 1개 슬롯 선택 + final sacrificeIndex = + equippedNonWeaponSlots[state.rng.nextInt(equippedNonWeaponSlots.length)]; + final slot = EquipmentSlot.values[sacrificeIndex]; + + // 해당 슬롯을 빈 장비로 교체 + newEquipment = newEquipment.setItemByIndex( + sacrificeIndex, + EquipmentItem.empty(slot), + ); + } // 사망 정보 생성 (전투 로그 포함) final deathInfo = DeathInfo( @@ -1689,7 +1722,7 @@ class ProgressService { ); return state.copyWith( - equipment: emptyEquipment, + equipment: newEquipment, progress: progress, deathInfo: deathInfo, ); diff --git a/lib/src/core/engine/reward_service.dart b/lib/src/core/engine/reward_service.dart index 7dfd718..e508385 100644 --- a/lib/src/core/engine/reward_service.dart +++ b/lib/src/core/engine/reward_service.dart @@ -1,25 +1,82 @@ import 'package:asciineverdie/src/core/engine/game_mutations.dart'; +import 'package:asciineverdie/src/core/engine/item_service.dart'; +import 'package:asciineverdie/src/core/engine/shop_service.dart'; +import 'package:asciineverdie/src/core/model/equipment_slot.dart'; import 'package:asciineverdie/src/core/model/game_state.dart'; -import 'package:asciineverdie/src/core/util/pq_logic.dart'; +import 'package:asciineverdie/src/core/model/pq_config.dart'; +import 'package:asciineverdie/src/core/util/pq_logic.dart' as pq_logic; /// Applies quest/act rewards to the GameState using shared RNG. class RewardService { - RewardService(this.mutations); + RewardService(this.mutations, this.config); final GameMutations mutations; + final PqConfig config; - GameState applyReward(GameState state, RewardKind reward) { + GameState applyReward(GameState state, pq_logic.RewardKind reward) { switch (reward) { - case RewardKind.spell: + case pq_logic.RewardKind.spell: return mutations.winSpell(state, state.stats.wis, state.traits.level); - case RewardKind.equip: - // 원본 Main.pas:797 - Random(Equips.Items.Count) (11개 슬롯) - final slotIndex = state.rng.nextInt(Equipment.slotCount); - return mutations.winEquipByIndex(state, state.traits.level, slotIndex); - case RewardKind.stat: + case pq_logic.RewardKind.equip: + return _applyEquipReward(state); + case pq_logic.RewardKind.stat: return mutations.winStat(state); - case RewardKind.item: + case pq_logic.RewardKind.item: return mutations.winItem(state); } } + + /// 장비 보상 처리 (개선된 로직) + /// + /// - 빈 슬롯: 무조건 장착 + /// - 기존 장비 있음: 점수 비교 → 업그레이드만 / 다운그레이드 시 판매 + GameState _applyEquipReward(GameState state) { + final slotIndex = state.rng.nextInt(Equipment.slotCount); + final slot = EquipmentSlot.values[slotIndex]; + final currentItem = state.equipment.getItemByIndex(slotIndex); + final level = state.traits.level; + + // 새 장비 생성 + final itemService = ItemService(rng: state.rng); + final name = pq_logic.winEquip(config, state.rng, level, slotIndex); + final newItem = itemService.generateEquipment( + name: name, + slot: slot, + level: level, + ); + + // 빈 슬롯이면 무조건 장착 + if (currentItem.isEmpty) { + return state.copyWith( + rng: state.rng, + equipment: state.equipment + .setItemByIndex(slotIndex, newItem) + .copyWith(bestIndex: slotIndex), + ); + } + + // 점수 비교 + final currentScore = ItemService.calculateEquipmentScore(currentItem); + final newScore = ItemService.calculateEquipmentScore(newItem); + + if (newScore > currentScore) { + // 업그레이드: 새 장비 장착 + return state.copyWith( + rng: state.rng, + equipment: state.equipment + .setItemByIndex(slotIndex, newItem) + .copyWith(bestIndex: slotIndex), + ); + } else { + // 다운그레이드: 새 장비 판매 (골드로 변환) + final shopService = ShopService(rng: state.rng); + final sellPrice = shopService.calculateSellPrice(newItem); + return state.copyWith( + rng: state.rng, + inventory: state.inventory.copyWith( + gold: state.inventory.gold + sellPrice, + ), + ); + } + } } diff --git a/lib/src/core/engine/test_character_service.dart b/lib/src/core/engine/test_character_service.dart new file mode 100644 index 0000000..3bb6ec1 --- /dev/null +++ b/lib/src/core/engine/test_character_service.dart @@ -0,0 +1,268 @@ +import 'package:asciineverdie/src/core/engine/item_service.dart'; +import 'package:asciineverdie/src/core/model/combat_stats.dart'; +import 'package:asciineverdie/src/core/model/equipment_item.dart'; +import 'package:asciineverdie/src/core/model/equipment_slot.dart'; +import 'package:asciineverdie/src/core/model/game_state.dart'; +import 'package:asciineverdie/src/core/model/hall_of_fame.dart'; +import 'package:asciineverdie/src/core/model/item_stats.dart'; +import 'package:asciineverdie/src/core/model/pq_config.dart'; +import 'package:asciineverdie/src/core/util/deterministic_random.dart'; +import 'package:asciineverdie/src/core/util/roman.dart'; + +/// 테스트 캐릭터 생성 서비스 (디버그 모드 전용) +/// +/// 현재 캐릭터를 레벨 100, 고급 장비, 다수의 스킬을 가진 +/// 캐릭터로 변환하여 명예의 전당에 등록할 수 있게 함. +class TestCharacterService { + TestCharacterService({ + required this.config, + required this.rng, + }); + + final PqConfig config; + final DeterministicRandom rng; + + /// 테스트 캐릭터 데이터 생성 + /// + /// 현재 GameState를 기반으로: + /// - 레벨 100 + /// - 장비: Rare+, 최소 Epic 2개, Legendary 1개 + /// - 스킬: 20~30개 랜덤 + HallOfFameEntry createTestCharacter(GameState state) { + // 1. 장비 생성 (Rare+, Epic 2+, Legendary 1+) + final equipment = _generateTestEquipment(); + + // 2. 스킬 생성 (20~30개) + final skills = _generateTestSkills(); + + // 3. 레벨 100 기준 스탯 계산 + final testStats = _generateTestStats(); + + // 4. CombatStats 계산 + final combatStats = CombatStats.fromStats( + stats: testStats, + equipment: equipment, + level: 100, + ); + + // 5. HallOfFameEntry 생성 + return HallOfFameEntry( + id: DateTime.now().millisecondsSinceEpoch.toString(), + characterName: state.traits.name, + race: state.traits.race, + raceId: state.traits.raceId, + klass: state.traits.klass, + level: 100, + totalPlayTimeMs: 36000000, // 10시간 (테스트용 기본값) + totalDeaths: rng.nextInt(50), // 0~49 랜덤 사망 횟수 + monstersKilled: 5000 + rng.nextInt(5000), // 5000~10000 처치 + questsCompleted: 100 + rng.nextInt(50), // 100~150 퀘스트 + clearedAt: DateTime.now(), + finalStats: combatStats, + finalEquipment: equipment.items, + finalSkills: skills, + ); + } + + /// 테스트용 장비 생성 + /// + /// 조건: + /// - 모든 장비 Rare 이상 + /// - 최소 Epic 2개 + /// - 최소 Legendary 1개 + Equipment _generateTestEquipment() { + final itemService = ItemService(rng: rng); + final items = []; + + // 희귀도 분배 결정 + // 11개 슬롯: 1 Legendary, 2 Epic, 나머지 Rare + final rarities = [ + ItemRarity.legendary, // 1개 + ItemRarity.epic, ItemRarity.epic, // 2개 + ItemRarity.rare, ItemRarity.rare, ItemRarity.rare, // 3개 + ItemRarity.rare, ItemRarity.rare, ItemRarity.rare, // 3개 + ItemRarity.rare, ItemRarity.rare, // 2개 + ]; + + // 희귀도 셔플 + _shuffleList(rarities); + + // 각 슬롯별 장비 생성 + for (var i = 0; i < Equipment.slotCount; i++) { + final slot = EquipmentSlot.values[i]; + final rarity = rarities[i]; + final name = _generateEquipmentName(slot, rarity); + + final item = itemService.generateEquipment( + name: name, + slot: slot, + level: 100, + rarity: rarity, + ); + items.add(item); + } + + // Legendary 슬롯을 bestIndex로 설정 + final legendaryIndex = rarities.indexOf(ItemRarity.legendary); + + return Equipment(items: items, bestIndex: legendaryIndex); + } + + /// 희귀도에 맞는 장비 이름 생성 + String _generateEquipmentName(EquipmentSlot slot, ItemRarity rarity) { + final prefixes = switch (rarity) { + ItemRarity.legendary => [ + 'Divine', + 'Mythical', + 'Godlike', + 'Eternal', + 'Transcendent', + ], + ItemRarity.epic => [ + 'Ancient', + 'Infernal', + 'Celestial', + 'Void', + 'Corrupted', + ], + ItemRarity.rare => [ + 'Superior', + 'Enchanted', + 'Reinforced', + 'Blessed', + 'Refined', + ], + _ => ['Standard', 'Basic', 'Common'], + }; + + final slotNames = switch (slot) { + EquipmentSlot.weapon => [ + 'Keyboard of Power', + 'Binary Blade', + 'Compiler Crusher', + ], + EquipmentSlot.shield => [ + 'Firewall Shield', + 'Exception Handler', + 'Null Guard', + ], + EquipmentSlot.helm => [ + 'Neural Helm', + 'Thought Processor', + 'Mind Buffer', + ], + EquipmentSlot.hauberk => [ + 'Matrix Armor', + 'Byte Mail', + 'Kernel Plate', + ], + EquipmentSlot.brassairts => [ + 'Bit Guards', + 'Stream Bracers', + 'Thread Wraps', + ], + EquipmentSlot.vambraces => [ + 'Code Vambraces', + 'Debug Cuffs', + 'Loop Guards', + ], + EquipmentSlot.gauntlets => [ + 'Input Gloves', + 'Handler Mitts', + 'Pointer Grips', + ], + EquipmentSlot.gambeson => [ + 'Layer Vest', + 'Cache Coat', + 'Buffer Jacket', + ], + EquipmentSlot.cuisses => [ + 'Register Guards', + 'Stack Protectors', + 'Heap Leggings', + ], + EquipmentSlot.greaves => [ + 'Runtime Greaves', + 'Compile Shins', + 'Execute Boots', + ], + EquipmentSlot.sollerets => [ + 'Boot Loader', + 'System Treads', + 'Process Soles', + ], + }; + + final prefix = prefixes[rng.nextInt(prefixes.length)]; + final slotName = slotNames[rng.nextInt(slotNames.length)]; + + return '$prefix $slotName'; + } + + /// 테스트용 스킬 생성 (20~30개) + List> _generateTestSkills() { + final spells = config.spells; + final skillCount = 20 + rng.nextInt(11); // 20~30개 + final selectedSpells = {}; + final skills = >[]; + + // 중복 없이 스킬 선택 + while (selectedSpells.length < skillCount && selectedSpells.length < spells.length) { + final index = rng.nextInt(spells.length); + final spell = spells[index].split('|')[0]; + if (selectedSpells.add(spell)) { + // 랭크: I~X 랜덤 (레벨 100 기준 높은 랭크 선호) + final rank = _randomHighRank(); + skills.add({'name': spell, 'rank': rank}); + } + } + + return skills; + } + + /// 높은 랭크 생성 (V~X 선호) + String _randomHighRank() { + // 5~10 범위 (V~X) + final rankValue = 5 + rng.nextInt(6); + return intToRoman(rankValue); + } + + /// 테스트용 스탯 생성 (레벨 100 기준) + Stats _generateTestStats() { + // 레벨 100 기준 높은 스탯 + // 기본 스탯: 50~80 범위 + final str = 50 + rng.nextInt(31); + final con = 50 + rng.nextInt(31); + final dex = 50 + rng.nextInt(31); + final int_ = 50 + rng.nextInt(31); + final wis = 50 + rng.nextInt(31); + final cha = 50 + rng.nextInt(31); + + // HP/MP 계산 (레벨 100 기준) + final hpMax = 500 + con * 10; + final mpMax = 200 + int_ * 5 + wis * 3; + + return Stats( + str: str, + con: con, + dex: dex, + intelligence: int_, + wis: wis, + cha: cha, + hpMax: hpMax, + mpMax: mpMax, + hpCurrent: hpMax, + mpCurrent: mpMax, + ); + } + + /// 리스트 셔플 (Fisher-Yates) + void _shuffleList(List list) { + for (var i = list.length - 1; i > 0; i--) { + final j = rng.nextInt(i + 1); + final temp = list[i]; + list[i] = list[j]; + list[j] = temp; + } + } +}