7월 22일, 팀원들과 백준(Baekjoon Online Judge) 문제 풀이 시간을 가졌다.
팀원이 직접 백준에 출제한 문제(백준 17081번)로 구현이 굉장히 복잡했다.
그리고 팀에서 정한 풀이 조건 역시 있었는데, 우선은 익숙하지 않은 C#으로 풀어야했다.
또한, 객체 지향 설계를 따라야 했고 테스트 코드도 작성해야 했다.
전체 팀원 10명을 4팀(2명, 2명, 3명, 3명)으로 나누어 문제를 풀었는데,
화면을 공유하며 동시 편집을 진행하다보니 훨씬 긴장(?)이 되었다.
익숙하지 않은 언어와 방식으로 풀려고하니 내 부족한 실력이 드러나는 것 같았기 때문이다.
어쨌든 오전 11시부터 오후 6시까지 열심히 문제를 풀었다.
솔직히 말하자면 풀이 과정에서 내가 기여한 내용은 거의 없었고 팀원이 대부분 해결하였다.
그래서 팀원에게 미안하기도 부끄럽기도 한 시간이었다.
처음에는 이러한 문제 풀이 시간이 굉장히 불편하고 힘들었다.
나의 부족함을 다른 사람과 직면하는 것이 쉬운 일은 아니었기 때문이다.
그러나 다 마치고 나니 나에게 필요했던 시간이 아닌가 하는 생각이 들었다.
내가 속한 회사가 곧 나의 실력을 증명한다는 착각과 함께
애써 나의 부족함을 인정하지 않으려 했던 요즘이었다.
분명 더 노력해야 함을 알고 있지만 이 정도면 충분하다는 자기 위로, 기만을 하고 있었다.
스스로를 점검하고, 돌아보는 시간이 괴롭지만 필요하지 않을까?
다음 문제 풀이 시간에는 조금 더 잘할 수 있기를!
using RPG_Extreme;
var rpg = new RPGExtremeProblem();
Console.Write(rpg.GetOutput());
namespace RPG_Extreme
{
public class RPGExtremeProblem : BaekJoonProblem
{
private int _monsterCount;
private int _itemCount;
private Position _initialPosition;
public RPGExtremeProblem() : base()
{
}
public RPGExtremeProblem(List<string> input) : base(input)
{
var rowCount = int.Parse(input.First().Split().First());
var monsterCount = 0;
var itemCount = 0;
for (int i = 1; i < rowCount + 1; i++)
{
var row = input.ElementAt(i);
monsterCount += row.Count(chr => chr == '&' || chr == 'M');
itemCount += row.Count(chr => chr == 'B');
var characterColPosition = row.IndexOf('@');
if (characterColPosition > -1)
{
_initialPosition = new Position { Row = i - 1, Col = characterColPosition };
}
}
_monsterCount = monsterCount;
_itemCount = itemCount;
}
protected override List<string> ReadInput()
{
var inputs = new List<string>();
var rowCol = Console.ReadLine();
inputs.Add(rowCol);
var rowCount = int.Parse(rowCol.Split().First());
var monsterCount = 0;
var itemCount = 0;
for (int i = 0; i < rowCount; i++)
{
var row = Console.ReadLine();
monsterCount += row.Count(chr => chr == '&' || chr == 'M');
itemCount += row.Count(chr => chr == 'B');
var characterColPosition = row.IndexOf('@');
if (characterColPosition > -1)
{
_initialPosition = new Position { Row = i, Col = characterColPosition };
}
inputs.Add(row);
}
_monsterCount = monsterCount;
_itemCount = itemCount;
inputs.Add(Console.ReadLine());
for (int i = 0; i < monsterCount; i++)
{
inputs.Add(Console.ReadLine());
}
for (int i = 0; i < itemCount; i++)
{
inputs.Add(Console.ReadLine());
}
return inputs;
}
protected override string Solution()
{
var rowCol = Input.First().Split();
var rowCount = int.Parse(rowCol.First());
var colCount = int.Parse(rowCol.Skip(1).First());
var grid = Input.Skip(1).Take(rowCount).ToList();
var commands = Input.Skip(1 + rowCount).First();
var monsterInfomations = Input.Skip(1 + rowCount + 1).Take(_monsterCount);
var monsters = monsterInfomations.Select(monsterInfomation =>
{
var splittedInfomation = monsterInfomation.Split();
var monsterRow = int.Parse(splittedInfomation.ElementAt(0)) - 1;
var monsterCol = int.Parse(splittedInfomation.ElementAt(1)) - 1;
var monsterName = splittedInfomation.ElementAt(2);
var monsterAttack = int.Parse(splittedInfomation.ElementAt(3));
var monsterDefense = int.Parse(splittedInfomation.ElementAt(4));
var monsterHP = int.Parse(splittedInfomation.ElementAt(5));
var monsterExperience = int.Parse(splittedInfomation.ElementAt(6));
if (grid.ElementAt(monsterRow).ElementAt(monsterCol) == '&')
{
return new Monster()
{
Position = new Position { Row = monsterRow, Col = monsterCol },
Name = monsterName,
Attack = monsterAttack,
Defence = monsterDefense,
MaxHP = monsterHP,
HP = monsterHP,
Experience = monsterExperience,
};
}
else
{
return new BossMonster()
{
Position = new Position { Row = monsterRow, Col = monsterCol },
Name = monsterName,
Attack = monsterAttack,
Defence = monsterDefense,
MaxHP = monsterHP,
HP = monsterHP,
Experience = monsterExperience,
};
}
});
var itemInfomations = Input.Skip(1 + rowCount + 1 + _monsterCount).Take(_itemCount);
var items = itemInfomations.Select(itemInfomation =>
{
var splittedInfomation = itemInfomation.Split();
var itemRow = int.Parse(splittedInfomation.ElementAt(0)) - 1;
var itemCol = int.Parse(splittedInfomation.ElementAt(1)) - 1;
var itemType = splittedInfomation.ElementAt(2);
var itemMetadata = splittedInfomation.ElementAt(3);
if (itemType == "W")
{
return new Weapon
{
Position = new Position { Row = itemRow, Col = itemCol },
Attack = int.Parse(itemMetadata),
} as Item;
}
else if (itemType == "A")
{
return new Armor
{
Position = new Position { Row = itemRow, Col = itemCol },
Defence = int.Parse(itemMetadata),
} as Item;
}
else
{
return new Accessory
{
Position = new Position { Row = itemRow, Col = itemCol },
Name = itemMetadata,
} as Item;
}
});
var character = new Character
{
CurrentPosition = _initialPosition,
InitialPosition = _initialPosition,
};
var turn = 0;
var prevSymbol = '.';
for (var i = 0; i < commands.Length; i++)
{
var command = commands[i];
turn++;
var currentPosition = character.CurrentPosition;
var nextPosition = new Position { Row = character.CurrentPosition.Row, Col = character.CurrentPosition.Col };
var isGameEnd = false;
var killer = string.Empty;
switch (command)
{
case 'L':
nextPosition.Col -= 1;
break;
case 'R':
nextPosition.Col += 1;
break;
case 'U':
nextPosition.Row -= 1;
break;
case 'D':
nextPosition.Row += 1;
break;
default:
throw new Exception();
}
if (nextPosition.Row >= rowCount
|| nextPosition.Row < 0
|| nextPosition.Col >= colCount
|| nextPosition.Col < 0
|| grid.ElementAt(nextPosition.Row).ElementAt(nextPosition.Col) == '#')
{
nextPosition = currentPosition;
}
var nextSymbol = grid.ElementAt(nextPosition.Row).ElementAt(nextPosition.Col);
if (nextSymbol == '^' || (nextSymbol == '@' && prevSymbol == '^'))
{
var revived = false;
character.DamagedBySpikeTrap();
(isGameEnd, revived) = character.CheckGameIsEnd();
if (isGameEnd)
{
killer = "SPIKE TRAP";
}
else
{
if (revived)
{
nextPosition = character.InitialPosition;
}
}
}
else if (nextSymbol == '&')
{
var monster = monsters.First(x => x.Position.Row == nextPosition.Row && x.Position.Col == nextPosition.Col);
var result = character.FightWith(monster);
if (result == FightResult.Lose)
{
(isGameEnd, _) = character.CheckGameIsEnd(monster);
if (isGameEnd)
{
killer = monster.Name;
}
else
{
nextPosition = character.CurrentPosition;
}
}
else
{
grid[nextPosition.Row] = grid[nextPosition.Row].Remove(nextPosition.Col, 1).Insert(nextPosition.Col, ".");
}
}
else if (nextSymbol == 'M')
{
var monster = monsters.First(x => x.Position.Row == nextPosition.Row && x.Position.Col == nextPosition.Col);
var result = character.FightWith(monster);
if (result == FightResult.Lose)
{
(isGameEnd, _) = character.CheckGameIsEnd(monster);
if (isGameEnd)
{
killer = monster.Name;
}
else
{
nextPosition = character.CurrentPosition;
}
}
else
{
grid[nextPosition.Row] = grid[nextPosition.Row].Remove(nextPosition.Col, 1).Insert(nextPosition.Col, ".");
isGameEnd = true;
}
}
else if (nextSymbol == 'B')
{
var item = items.First(x => x.Position.Row == nextPosition.Row && x.Position.Col == nextPosition.Col);
if (item is Weapon)
{
character.Weapon = item as Weapon;
}
else if (item is Armor)
{
character.Armor = item as Armor;
}
else
{
if (character.isAbleToEquipAccessory(item as Accessory))
{
character.Accessories.Add(item as Accessory);
}
}
grid[nextPosition.Row] = grid[nextPosition.Row].Remove(nextPosition.Col, 1).Insert(nextPosition.Col, ".");
}
grid[currentPosition.Row] = grid[currentPosition.Row].Replace('@', prevSymbol);
if (string.IsNullOrEmpty(killer))
{
prevSymbol = grid[nextPosition.Row][nextPosition.Col];
grid[nextPosition.Row] = grid[nextPosition.Row].Remove(nextPosition.Col, 1).Insert(nextPosition.Col, "@");
character.CurrentPosition = nextPosition;
}
if (isGameEnd)
{
return string.IsNullOrEmpty(killer) ? GetResult(character, grid, turn) + "YOU WIN!" : GetResult(character, grid, turn) + $"YOU HAVE BEEN KILLED BY {killer}..";
}
}
return GetResult(character, grid, turn) + "Press any key to continue.";
}
private string GetResult(Character character, List<string> grid, int turn)
{
var remainingHP = character.CurrentHP < 0 ? 0 : character.CurrentHP;
return string.Join(Environment.NewLine, grid) + Environment.NewLine +
$"Passed Turns : {turn}" + Environment.NewLine +
$"LV : {character.Level}" + Environment.NewLine +
$"HP : {remainingHP}/{character.MaxHP}" + Environment.NewLine +
$"ATT : {character.BaseAttack}+{character.Weapon?.Attack ?? 0}" + Environment.NewLine +
$"DEF : {character.BaseDefence}+{character.Armor?.Defence ?? 0}" + Environment.NewLine +
$"EXP : {character.Experience}/{character.Level * 5}" + Environment.NewLine;
}
public class Item
{
public Position Position { get; set; }
}
public class Weapon : Item
{
public int Attack { get; set; }
}
public class Armor : Item
{
public int Defence { get; set; }
}
public class Accessory : Item
{
public string Name { get; set; }
}
public enum FightResult
{
Win,
Lose,
}
public class Character
{
private const int _maxAccessoriesCount = 4;
public int BaseAttack { get; set; } = 2;
public int BaseDefence { get; set; } = 2;
public Position CurrentPosition { get; set; }
public Position InitialPosition { get; set; }
public int Level { get; set; } = 1;
public int MaxHP { get; set; } = 20;
public int CurrentHP { get; set; } = 20;
public (bool IsEnd, bool Revived) CheckGameIsEnd(Monster killer = null)
{
if (CurrentHP > 0)
{
return (false, false);
}
if (HasAccessory("RE"))
{
CurrentHP = MaxHP;
CurrentPosition = InitialPosition;
var index = Accessories.FindIndex(x => x.Name == "RE");
Accessories.RemoveAt(index);
if (killer is not null)
{
killer.HP = killer.MaxHP;
}
return (false, true);
}
else
{
return (true, false);
}
}
public FightResult FightWith(Monster monster)
{
if (monster is BossMonster && HasAccessory("HU"))
{
CurrentHP = MaxHP;
}
var isFirstTurn = true;
while (CurrentHP > 0 && monster.HP > 0)
{
monster.HP -= Math.Max(1, GetAttackDamage(isFirstTurn) - monster.Defence);
if (monster.HP <= 0)
{
GetExperience(monster.Experience);
if (HasAccessory("HR"))
{
CurrentHP = Math.Min(MaxHP, CurrentHP + 3);
}
return FightResult.Win;
}
DamagedByMonster(monster, isFirstTurn);
if (CurrentHP <= 0)
{
return FightResult.Lose;
}
isFirstTurn = false;
}
throw new Exception();
}
private void DamagedByMonster(Monster monster, bool isFirstTurn)
{
if (monster is BossMonster && isFirstTurn && HasAccessory("HU"))
{
CurrentHP -= 0;
}
else
{
CurrentHP -= Math.Max(1, monster.Attack - GetDefence());
}
}
public void DamagedBySpikeTrap()
{
if (HasAccessory("DX"))
{
CurrentHP -= 1;
}
else
{
CurrentHP -= 5;
}
}
public int GetAttackDamage(bool isFirstAttack = false)
{
var weaponAttack = Weapon?.Attack ?? 0;
var attackDamage = BaseAttack + weaponAttack;
return isFirstAttack ? attackDamage * GetAccessoriesAttackEffect() : attackDamage;
}
public int GetAccessoriesAttackEffect()
{
if (HasAccessory("CO"))
{
if (HasAccessory("DX"))
{
return 3;
}
return 2;
}
return 1;
}
private bool HasAccessory(string name)
{
return Accessories.Any(x => x.Name == name);
}
public int GetDefence()
{
var armorDefence = Armor?.Defence ?? 0;
return BaseDefence + armorDefence;
}
public int Experience { get; set; } = 0;
public Weapon Weapon { get; set; }
public Armor Armor { get; set; }
public List<Accessory> Accessories { get; set; } = new List<Accessory>();
public void GetExperience(int experience)
{
if (HasAccessory("EX"))
{
Experience += (int)(experience * 1.2);
}
else
{
Experience += experience;
}
if (Level * 5 <= Experience)
{
LevelUp();
}
}
public void LevelUp()
{
Level++;
Experience = 0;
MaxHP += 5;
CurrentHP = MaxHP;
BaseAttack += 2;
BaseDefence += 2;
}
public bool isAbleToEquipAccessory(Accessory accessory)
{
return Accessories.Count() < _maxAccessoriesCount && !Accessories.Any(x => x.Name == accessory.Name);
}
}
public class Position
{
public int Row { get; set; }
public int Col { get; set; }
}
public class Monster
{
public Position Position { get; set; }
public string Name { get; set; }
public int Attack { get; set; }
public int Defence { get; set; }
public int MaxHP { get; set; }
public int HP { get; set; }
public int Experience { get; set; }
}
public class BossMonster : Monster
{
}
}
}
namespace RPG_Extreme
{
public abstract class BaekJoonProblem : Problem<List<string>, string>
{
protected BaekJoonProblem() : base()
{
}
protected BaekJoonProblem(List<string> input) : base(input)
{
}
}
}
namespace RPG_Extreme
{
public abstract class Problem<TInput, TOutput>
{
protected TInput? Input { get; init; }
protected Problem()
{
Input = ReadInput();
}
protected Problem(TInput input)
{
Input = input;
}
public TOutput GetOutput()
{
return Solution();
}
protected abstract TInput ReadInput();
protected abstract TOutput Solution();
}
}