본문으로 건너뛰기

· 약 8분
karais89

Instructions

링크

This is the first of my "-nacci" series. If you like this kata, check out the zozonacci sequence too.

Task

  1. Mix -nacci sequences using a given pattern p.
  2. Return the first n elements of the mixed sequence.

Rules

  1. The pattern p is given as a list of strings (or array of symbols in Ruby) using the pattern mapping below (e. g. ['fib', 'pad', 'pel'] means take the next fibonacci, then the next padovan, then the next pell number and so on).
  2. When n is 0 or p is empty return an empty list.
  3. If the length of p is more than n repeat the pattern.

Examples

            0 1 2 3 4
----------+------------------
fibonacci:| 0, 1, 1, 2, 3 ...
padovan: | 1, 0, 0, 1, 0 ...
pell: | 0, 1, 2, 5, 12 ...

pattern = ['fib', 'pad', 'pel']
n = 6
# ['fib', 'pad', 'pel', 'fib', 'pad', 'pel']
# result = [fibonacci(0), padovan(0), pell(0), fibonacci(1), padovan(1), pell(1)]
result = [0, 1, 0, 1, 0, 1]

pattern = ['fib', 'fib', 'pel']
n = 6
# ['fib', 'fib', 'pel', 'fib', 'fib', 'pel']
# result = [fibonacci(0), fibonacci(1), pell(0), fibonacci(2), fibonacci(3), pell(1)]
result = [0, 1, 0, 1, 2, 1]

Sequences

  • fibonacci : 0, 1, 1, 2, 3 ...
  • padovan: 1, 0, 0, 1, 0 ...
  • jacobsthal: 0, 1, 1, 3, 5 ...
  • pell: 0, 1, 2, 5, 12 ...
  • tribonacci: 0, 0, 1, 1, 2 ...
  • tetranacci: 0, 0, 0, 1, 1 ...

Pattern mapping

  • 'fib' -> fibonacci
  • 'pad' -> padovan
  • 'jac' -> jacobstahl
  • 'pel' -> pell
  • 'tri' -> tribonacci
  • 'tet' -> tetranacci

If you like this kata, check out the zozonacci sequence.

My Solution

using System;
using System.Numerics;
using System.Collections.Generic;

namespace Solution
{
public static class Kata
{
// non-recursive
// Dynamic Programming
public static BigInteger Fibonacci(int n)
{
if (n < 2)
{
return n;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 0;
arr[1] = 1;

for (int i = 2; i <= n; i++)
{
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}

// a(n) = a(n-2) + a(n-3) with a(0)=1, a(1)=a(2)=0.
public static BigInteger Padovan(int n)
{
if (n == 0)
{
return 1;
}

if (n <= 2)
{
return 0;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 1;
arr[1] = 0;
arr[2] = 0;

for (int i = 3; i <= n; i++)
{
arr[i] = arr[i - 2] + arr[i - 3];
}
return arr[n];
}

// a(n) = a(n-1) + 2*a(n-2), with a(0) = 0, a(1) = 1.
public static BigInteger Jacobstahl(int n)
{
if (n < 2)
{
return n;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 0;
arr[1] = 1;

for (int i = 2; i <= n; i++)
{
arr[i] = arr[i - 1] + 2 * arr[i - 2];
}
return arr[n];
}

// a(0) = 0, a(1) = 1; for n > 1, a(n) = 2*a(n-1) + a(n-2).
public static BigInteger Pell(int n)
{
if (n < 2)
{
return n;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 0;
arr[1] = 1;

for (int i = 2; i <= n; i++)
{
arr[i] = 2 * arr[i - 1] + arr[i - 2];
}
return arr[n];
}

// a(n) = a(n-1) + a(n-2) + a(n-3) with a(0)=a(1)=0, a(2)=1.
public static BigInteger Tribonacci(int n)
{
if (n == 0 || n == 1)
{
return 0;
}

if (n == 2)
{
return 1;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 0;
arr[1] = 0;
arr[2] = 1;

for (int i = 3; i <= n; i++)
{
arr[i] = arr[i - 1] + arr[i - 2] + arr[i - 3];
}
return arr[n];
}

// a(n) = a(n-1) + a(n-2) + a(n-3) + a(n-4) with a(0)=a(1)=a(2)=0, a(3)=1.
public static BigInteger Tetranacci(int n)
{
if (n < 3)
{
return 0;
}

if (n == 3)
{
return 1;
}

BigInteger[] arr = new BigInteger[n + 1];
arr[0] = 0;
arr[1] = 0;
arr[2] = 0;
arr[3] = 1;

for (int i = 4; i <= n; i++)
{
arr[i] = arr[i - 1] + arr[i - 2] + arr[i - 3] + arr[i - 4];
}
return arr[n];
}

public static void ShowConsole(string head, Func<int, BigInteger> func, int n)
{
Console.Write(head + " : ");
for (int i = 0; i < n; i++)
{
Console.Write(" " + func(i));
}
Console.WriteLine();
}

public static BigInteger[] Mixbonacci(string[] pattern, int length)
{
/*
ShowConsole("fib", Fibonacci, 10);
ShowConsole("pad", Padovan, 10);
ShowConsole("jac", Jacobstahl, 10);
ShowConsole("pel", Pell, 10);
ShowConsole("tri", Tribonacci, 10);
ShowConsole("tet", Tetranacci, 10);
*/
if (length == 0 || pattern == null || pattern.Length == 0)
{
return new BigInteger[] {};
}

Dictionary<string, int> boancciCounts = new Dictionary<string, int>();
boancciCounts["fib"] = 0;
boancciCounts["pad"] = 0;
boancciCounts["jac"] = 0;
boancciCounts["pel"] = 0;
boancciCounts["tri"] = 0;
boancciCounts["tet"] = 0;

Dictionary<string, Func<int, BigInteger>> boancciFuncs = new Dictionary<string, Func<int, BigInteger>>();
boancciFuncs["fib"] = Fibonacci;
boancciFuncs["pad"] = Padovan;
boancciFuncs["jac"] = Jacobstahl;
boancciFuncs["pel"] = Pell;
boancciFuncs["tri"] = Tribonacci;
boancciFuncs["tet"] = Tetranacci;


BigInteger[] mixbonaccis = new BigInteger[length];
for (int i = 0; i < length; i++)
{
string key = pattern[i % pattern.Length];
mixbonaccis[i] = boancciFuncs[key](boancciCounts[key]++);
}

return mixbonaccis;
}
}
}
  • 여러가지 점화식이 있는 함수들로 수를 표현하면 되는 문제.
  • 여러가지 규칙의 함수들이 있다.
  • 사실 문제 자체는 재귀 함수를 사용하는 방식이 가장 풀기 쉬운 방식인데, 스택 문제와 퍼포먼스 문제 때문에 해당 방법은 사용하면 안되는 듯 하다.
  • Func 까지 써가면서 품..
  • 재귀함수가 아닌 다이나믹 프로그래밍 방식을 사용함 (배열 사용)
  • 쓸데없이 길어지는 느낌이 없지 않아 있다.

Best Practices

using System.Numerics;
using System.Collections.Generic;
namespace Solution
{
public static class Kata
{
private static readonly Dictionary<string, IEnumerable<BigInteger>> GeneratorMapping =
new Dictionary<string, IEnumerable<BigInteger>>() {
{"fib", FibonacciGenerator()},
{"pad", PadovanGenerator()},
{"jac", JacobstahlGenerator()},
{"tet", TetranacciGenerator()},
{"tri", TribonacciGenerator()},
{"pel", PellGenerator()}
};

public static BigInteger[] Mixbonacci(string[] pattern, int length)
{
if (pattern.Length == 0 || length == 0)
{
return new BigInteger[] { };
}

var res = new List<BigInteger>() { };
var gens = new Dictionary<string, IEnumerator<BigInteger>>();
var patLength = pattern.Length;

for (var i = 0; i < patLength; i++)
{
var v = pattern[i];
gens[v] = GeneratorMapping[v].GetEnumerator();
}

for (var i = 0; i < length; i++)
{
var gen = gens[pattern[i % patLength]];
gen.MoveNext();
res.Add(gen.Current);
}

return res.ToArray();

}

public static IEnumerable<BigInteger> FibonacciGenerator()
{
var a = new BigInteger(0);
var b = new BigInteger(1);
BigInteger x;
while (true)
{
yield return a;
x = a;
a = b;
b = x + a;
}
}

public static IEnumerable<BigInteger> PadovanGenerator()
{
var a = new BigInteger(1);
var b = new BigInteger(0);
var c = new BigInteger(0);
BigInteger x;
BigInteger y;
while (true)
{
yield return a;
x = a;
y = b;
a = b;
b = c;
c = x + y;
}
}

public static IEnumerable<BigInteger> JacobstahlGenerator()
{
var a = new BigInteger(0);
var b = new BigInteger(1);
BigInteger x;
while (true)
{
yield return a;
x = a;
a = b;
b = 2 * x + b;
}
}


public static IEnumerable<BigInteger> PellGenerator()
{
var a = new BigInteger(0);
var b = new BigInteger(1);
BigInteger x;
while (true)
{
yield return a;
x = a;
a = b;
b = x + 2 * b;
}
}

public static IEnumerable<BigInteger> TribonacciGenerator()
{
var a = new BigInteger(0);
var b = new BigInteger(0);
var c = new BigInteger(1);
BigInteger x, y, z;
while (true)
{
yield return a;
x = a; y = b; z = c;
a = b; b = c;
c = x + y + z;
}
}

public static IEnumerable<BigInteger> TetranacciGenerator()
{
var a = new BigInteger(0);
var b = new BigInteger(0);
var c = new BigInteger(0);
var d = new BigInteger(1);
BigInteger x, y, z, j;
while (true)
{
yield return a;
x = a; y = b; z = c; j = d;
a = b; b = c; c = d;
d = x + y + z + j;
}
}
}
}
  • IEnumerable 특성을 이용해서 해결.

· 약 2분
karais89

Instructions

링크

You live in the city of Cartesia where all roads are laid out in a perfect grid. You arrived ten minutes too early to an appointment, so you decided to take the opportunity to go for a short walk. The city provides its citizens with a Walk Generating App on their phones -- everytime you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']). You always walk only a single block in a direction and you know it takes you one minute to traverse one city block, so create a function that will return true if the walk the app gives you will take you exactly ten minutes (you don't want to be early or late!) and will, of course, return you to your starting point. Return false otherwise.

Note: you will always receive a valid array containing a random assortment of direction letters ('n', 's', 'e', or 'w' only). It will never give you an empty array (that's not a walk, that's standing still!).

My Solution

public class Kata
{
public static bool IsValidWalk(string[] walk)
{
//insert brilliant code here
if (walk.Length != 10)
{
return false;
}

int dirX = 0, dirY = 0;
for (int i = 0; i < walk.Length; i++)
{
switch (walk[i])
{
case "n":
dirY++;
break;
case "s":
dirY--;
break;
case "e":
dirX++;
break;
case "w":
dirX--;
break;
default:
// nothing
break;
}
}

return dirX == 0 && dirY == 0;
}
}
  • 배열의 길이가 10이고, 모두 이동한 후에 제자리 걸음이면 true 아니면 false 이다.

Best Practices

public class Kata
{
public static bool IsValidWalk(string[] walk)
{
if (walk.Length != 10) return false;
var x = 0; var y = 0;
foreach (var dir in walk)
{
if (dir == "n") x++;
else if (dir == "s") x--;
else if (dir == "e") y++;
else if (dir == "w") y--;
}
return x == 0 && y == 0;
}
}
  • for와 foreach 차이 if 와 switch 차이 정도만 다르고 논리는 똑같다.

· 약 5분
karais89

Instructions

링크

In this kata you have to create a domain name validator mostly compliant with RFC 1035, RFC 1123, and RFC 2181

For purposes of this kata, following rules apply:

  • Domain name may contain subdomains (levels), hierarchically separated by . (period) character
  • Domain name must not contain more than 127 levels, including top level (TLD)
  • Domain name must not be longer than 253 characters (RFC specifies 255, but 2 characters are reserved for trailing dot and null character for root level)
  • Level names must be composed out of lowercase and uppercase ASCII letters, digits and - (minus sign) character
  • Level names must not start or end with - (minus sign) character
  • Level names must not be longer than 63 characters
  • Top level (TLD) must not be fully numerical

Additionally, in this kata

  • Domain name must contain at least one subdomain (level) apart from TLD
  • Top level validation must be naive - ie. TLDs nonexistent in IANA register are still considered valid as long as they adhere to the rules given above. The validation function accepts string with the full domain name and returns boolean value indicating whether the domain name is valid or not.

Examples:

validate('codewars') == False
validate('g.co') == True
validate('codewars.com') == True
validate('CODEWARS.COM') == True
validate('sub.codewars.com') == True
validate('codewars.com-') == False
validate('.codewars.com') == False
validate('example@codewars.com') == False
validate('127.0.0.1') == False

My Solution

using System;
using System.Text.RegularExpressions;
public class DomainNameValidator {
public bool validate(string domain) {
Console.WriteLine(domain);
// Domain name must not be longer than 253 characters (RFC specifies 255, but 2 characters are reserved for trailing dot and null character for root level)
if (domain.Length > 253)
{
return false;
}
string[] domainSplitDots = domain.Split(".");
// Domain name must contain at least one subdomain (level) apart from TLD
if (domainSplitDots.Length <= 1)
{
return false;
}
// all integer check
bool isAllInteger = true;
for (int i = 0; i < domainSplitDots.Length; i++)
{
string str = domainSplitDots[i];
int domainInteger = 0;
if (!int.TryParse(str, out domainInteger))
{
isAllInteger = false;
}
}
for (int i = 0; i < domainSplitDots.Length; i++)
{
string str = domainSplitDots[i];
int domainInteger = 0;
if (isAllInteger && int.TryParse(str, out domainInteger))
{
// Domain name must not contain more than 127 levels, including top level (TLD)
if (domainInteger >= 127)
{
return false;
}
}
else
{
// Level names must not be longer than 63 characters
if (str.Length > 63)
{
return false;
}
// Level names must be composed out of lowercase and uppercase ASCII letters, digits and - (minus sign) character
// https://stackoverflow.com/questions/1181419/verifying-that-a-string-contains-only-letters-in-c-sharp
bool isMatch = Regex.IsMatch(str, @"^[a-zA-Z0-9-]+$");
if (!isMatch)
{
Console.WriteLine(str);
return false;
}
// Level names must not start or end with - (minus sign) character
if (str.StartsWith("-") || str.EndsWith("-"))
{
Console.WriteLine(str);
return false;
}
}
}
// where is rules??
if (!isAllInteger)
{
string str = domainSplitDots[domainSplitDots.Length-1];
int ret = 0;
if (int.TryParse(str, out ret))
{
return false;
}
}
return true;
}
}
  • 도메인 이름 규칙
    • 점(.) 기호로 도메인이 구분된다.
    • 레벨은 127 이상을 포함할 수 없다.
    • 도메인 이름은 253 글자보다 반드시 작아야 한다.
    • 레벨 이름은 소문자, 대문자, 아스키 문자, 숫자 및 대쉬(-) 문자로 구성되어야 한다.
    • 레벨 이름은 대쉬(-)로 시작하거나 끝날 수 없다.
    • 레벨 이름은 63 글자를 초과할 수 없다.
    • 최상위 레벨은 완전히 숫자여야 한다.
    • 도메인 이름은 TLD를 제외한 하나 이상의 하위 도메인이 포함되어야 한다.
    • 최상위 유효성 검사는 순진해야합니다. 즉. IANA 등록부에 존재하지 않는 TLD는 위에 주어진 규칙을 준수하는 한 유효한 것으로 간주됩니다.
  • 문제를 잘못 이해해서 잘못 푼 부분이 있다.
  • 127 leves 부분을 잘못 이해 했음.

정규 표현식은 나중에 포스트로 정리를 해야 겠다.

Best Practices

using System;
using System.Text.RegularExpressions;
public class DomainNameValidator {
public bool validate(string domain) {
if (domain.Length > 253)
return false;
Regex re = new Regex(@"^(?!-)[a-z0-9-]{1,63}(?<!-)(?:\.(?!-)[a-z0-9-]{1,63}(?<!-)){0,125}\.(?!-)(?![0-9]+$)[a-z0-9-]{1,63}(?<!-)$", RegexOptions.IgnoreCase);
Match m = re.Match(domain);
return m.Success;
}
}
  • 정규 표현식 관련 문제를 많이 푸는 것 같다.
  • 도메인 규칙같은건 확실히 정규표현식을 사용하는게 맞다.

g0dm0d3's Solution

using System;
using System.Text.RegularExpressions;
using System.Linq;
public class DomainNameValidator {
public bool validate(string domain) {
if (domain.Length < 3 || domain.Length > 253) return false;
var levels = domain.Split('.');
if (levels.Count() < 2 || levels.Count() > 127) return false;
var zone = levels.Last();
if (Regex.IsMatch(zone, @"^[0-9]+$")) return false;
foreach (var level in levels) {
if (level.Length > 63) return false;
if (!Regex.IsMatch(level, @"^[a-zA-Z0-9\-]+$")) return false;
if (level.StartsWith("-") || level.EndsWith("-")) return false;
}
return true;
}
}
  • 표 자체는 받지 못한 해결책이다.
  • 그나마 가장 이해하기 쉬운 코드인것 같아서 가져왔다.

· 약 2분
karais89

Instructions

링크

You have to give the number of different integer triangles with one angle of 120 degrees which perimeters are under or equal a certain value. Each side of an integer triangle is an integer value.

give_triang(max. perimeter) --------> number of integer triangles,

with sides a, b, and c integers such that:

a + b + c <= max. perimeter

See some of the following cases

give_triang(5) -----> 0 # No Integer triangles with perimeter under or equal five
give_triang(15) ----> 1 # One integer triangle of (120 degrees). It's (3, 5, 7)
give_triang(30) ----> 3 # Three triangles: (3, 5, 7), (6, 10, 14) and (7, 8, 13)
give_triang(50) ----> 5 # (3, 5, 7), (5, 16, 19), (6, 10, 14), (7, 8, 13) and (9, 15, 21) are the triangles with perim under or equal 50.
  • 서로 다른 길이의 삼각형을 지정해야 된다.
  • 삼각형 중 하나는 120도 각도 이고, 둘레가 max 값 보다 작은 정수 삼각형을 구해야 한다.

My Solution

문제를 풀지 못했다. 정확한 문제 이해가 되지 않은 상태 였다.

Best Practices

using System;
public class IntTriangles
{
// Integer triangles with 120° angle are such that a² + ab + b² = c²
// http://www.had2know.com/academics/integer-triangles-120-degree-angle.html
public static int GiveTriang(int per)
{
var count = 0;
for(var a = 1; a < per; a++)
{
for(var b = a+1; a+b < per; b++)
{
var left = a * a + a * b + b * b;
var c = (int)Math.Sqrt(left);
if (left == c*c && c > b && a+b+c <= per)
{
count++;
}
}
}
return count;
}

}

· 약 5분
karais89

Instructions

링크

Introduction

There is a war and nobody knows - the alphabet war! The letters hide in their nuclear shelters. The nuclear strikes hit the battlefield and killed a lot of them.

Task

Write a function that accepts battlefield string and returns letters that survived the nuclear strike.

  • The battlefield string consists of only small letters, #,[ and ].
  • The nuclear shelter is represented by square brackets []. The letters inside the square brackets represent letters inside the shelter.
  • The # means a place where nuclear strike hit the battlefield. If there is at least one # on the battlefield, all letters outside of shelter die. When there is no any # on the battlefield, all letters survive (but do not expect such scenario too often ;-P ).
  • The shelters have some durability. When 2 or more # hit close to the shelter, the shelter is destroyed and all letters inside evaporate. The 'close to the shelter' means on the ground between the shelter and the next shelter (or beginning/end of battlefield). The below samples make it clear for you.

Example

abde[fgh]ijk => "abdefghijk" (all letters survive because there is no # )
ab#de[fgh]ijk => "fgh" (all letters outside die because there is a # )
ab#de[fgh]ij#k => "" (all letters dies, there are 2 # close to the shellter )
##abde[fgh]ijk => "" (all letters dies, there are 2 # close to the shellter )
##abde[fgh]ijk[mn]op => "mn" (letters from the second shelter survive, there is no # close)
#ab#de[fgh]ijk[mn]op => "mn" (letters from the second shelter survive, there is no # close)
#abde[fgh]i#jk[mn]op => "mn" (letters from the second shelter survive, there is only 1 # close)
[a]#[b]#[c] => "ac"
[a]#b#[c][d] => "d"
[a][b][c] => "abc"
##a[a]b[c]# => "c"

Alphabet war Collection

  • Alphavet war
  • Alphabet war - airstrike - letters massacre
  • Alphabet wars - reinforces massacre
  • Alphabet wars - nuclear strike
  • Alphabet war - Wo lo loooooo priests join the war

My Solution

 using System;
using System.Text;

public class Kata
{
// battleFiledChar = '#', '[', ']'
public static string RemoveBattleFieldChar(string b)
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < b.Length; i++)
{
if (b[i] == '#' || b[i] == '[' || b[i] == ']')
{
continue;
}
builder.Append(b[i]);
}
return builder.ToString();
}

public static string DestoryBracketsOutLetters(string b)
{
StringBuilder rmBuilder = new StringBuilder();
bool startBrackets = false;
for (int i = 0; i < b.Length; i++)
{
switch (b[i])
{
case '#':
rmBuilder.Append(b[i]);
break;
case '[':
startBrackets = true;
rmBuilder.Append(b[i]);
break;
case ']':
startBrackets = false;
rmBuilder.Append(b[i]);
break;
default:
break;
}

if (startBrackets)
{
if (b[i] != '#' && b[i] != '[' && b[i] != ']')
{
rmBuilder.Append(b[i]);
}
}
}

Console.WriteLine("rmBuilder: " + rmBuilder);
return rmBuilder.ToString();
}

public static string CheckSideSharpBrackets(string b)
{
StringBuilder strBuilder = new StringBuilder();
int index = 0;
while (true)
{
if (index >= b.Length)
{
break;
}

// startBreackets
if (b[index] == '[')
{
// left sharp
int sharpCnt = 0;
int leftIdx = index;
while (true)
{
if (leftIdx < 0 || b[leftIdx] == ']')
{
break;
}

if (b[leftIdx] == '#')
{
sharpCnt++;
}

leftIdx--;
}

// end brackets index
int endIdx = index;
while (true)
{
endIdx++;
if (b.Length <= endIdx)
{
// parssing error
break;
}

if (b[endIdx] == ']')
{
break;
}
}

// rightSideCheck
int rightIdx = endIdx;
while (true)
{
if (rightIdx >= b.Length || b[rightIdx] == '[')
{
break;
}

if (b[rightIdx] == '#')
{
sharpCnt++;
}

rightIdx++;
}

if (sharpCnt >= 2)
{
index = endIdx;
}
Console.WriteLine("sharpCnt: " + sharpCnt);
}

strBuilder.Append(b[index]);
index++;
}
Console.WriteLine("strBuilder: " + strBuilder);
return RemoveBattleFieldChar(strBuilder.ToString());
}

public static string AlphabetWar(string b)
{
Console.WriteLine("b: " + b);

int index = b.IndexOf('#');
if (index == -1)
{
return RemoveBattleFieldChar(b);
}

// step 1 : destroy not brackets letters
string str = DestoryBracketsOutLetters(b);

// step 2 : brackets side check
string ret = CheckSideSharpBrackets(str);
return ret;
}
}
  • #이 하나도 없을시에는 #과 괄호 기호들을 제거하고 그대로 출력
  • #이 하나라도 있을시 괄호 기호안의 알파벳을 제외하고 모두 제거
  • 제거된 문자열로 괄호 양 옆을 조사한 후 #의 개수에 따라 로직 처리
  • 문제를 보자마자 정규표현식을 써야겠다는 느낌은 옴.

Best Practices 1

using System;
using System.Text.RegularExpressions;
using System.Linq;
public class Kata
{
public static string AlphabetWar(string b)
=> !b.Contains('#') ? Regex.Replace(b,@"[\[\]]","") :
string.Concat(Regex.Matches(b, @"(?<=([a-z#]*))\[([a-z]+)\](?=([a-z#]*))").Cast<Match>().Where(g => (g.Groups[1].Value + g.Groups[3].Value).Count(c => c == '#') < 2).Select(g => g.Groups[2].Value));
}
  • 정규표현식과 Linq의 조합으로 코드가 짧아진다.

Best Practices 2

using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

public class Kata {
public static string AlphabetWar( string battleField ) {
const char strike = '#';
var underAttack = battleField.Contains( strike );
if ( !underAttack ) {
return battleField.Replace( "[", string.Empty ).Replace( "]", string.Empty );
}
var survivors = new StringBuilder();
var shelterAreaRegex = new Regex( @"(?'s1'[^\]]*)\[(?'s'[a-z]+)\](?'s2'[^\[]*)" );
while ( shelterAreaRegex.IsMatch( battleField ) ) {
var m = shelterAreaRegex.Match( battleField );
var shelterPopulation = m.Groups [ "s" ].Value;
var frontStrikesCount = m.Groups [ "s1" ].Value.Count( c => c == strike );
var behindStrikesCount = m.Groups [ "s2" ].Value.Count( c => c == strike );
if ( frontStrikesCount + behindStrikesCount < 2 ) {
survivors.Append( shelterPopulation );
}
battleField = battleField.Replace( m.Value, m.Groups [ "s2" ].Value );
}
return survivors.ToString( );
}
}
  • 위 코드보다는 가독성이 더 나은듯.

· 약 10분
karais89

Instructions

링크

The internet is a very confounding place for some adults. Tom has just joined an online forum and is trying to fit in with all the teens and tweens. It seems like they're speaking in another language! Help Tom fit in by translating his well-formatted English into n00b language.

The following rules should be observed:

  • "to" and "too" should be replaced by the number 2, even if they are only part of a word (E.g. today = 2day)
  • Likewise, "for" and "fore" should be replaced by the number 4
  • Any remaining double o's should be replaced with zeros (E.g. noob = n00b)
  • "be", "are", "you", "please", "people", "really", "have", and "know" should be changed to "b", "r", "u", "plz", "ppl", "rly", "haz", and "no" respectively (even if they are only part of the word)
  • When replacing words, always maintain case of the first letter unless another rule forces the word to all caps.
  • The letter "s" should always be replaced by a "z", maintaining case
  • "LOL" must be added to the beginning of any input string starting with a "w" or "W"
  • "OMG" must be added to the beginning (after LOL, if applicable,) of a string 32 characters(1) or longer
  • All evenly numbered words(2) must be in ALL CAPS (Example: Cake is very delicious. becomes Cake IZ very DELICIOUZ)
  • If the input string starts with "h" or "H", the entire output string should be in ALL CAPS
  • Periods ( . ), commas ( , ), and apostrophes ( ' ) are to be removed
  • (3)A question mark ( ? ) should have more question marks added to it, equal to the number of words2 in the sentence (Example: Are you a foo? has 4 words, so it would be converted to r U a F00????)
  • (3)Similarly, exclamation points ( ! ) should be replaced by a series of alternating exclamation points and the number 1, equal to the number of words(2) in the sentence (Example: You are a foo! becomes u R a F00!1!1)

1 Characters should be counted After: any word conversions, adding additional words, and removing punctuation. Excluding: All punctuation and any 1's added after exclamation marks ( ! ). Character count includes spaces.

2 For the sake of this kata, "words" are simply a space-delimited substring, regardless of its characters. Since the output may have a different number of words than the input, words should be counted based on the output string.

Example: whoa, you are my 123 <3 becomes LOL WHOA u R my 123 <3 = 7 words

3 The incoming string will be punctuated properly, so punctuation does not need to be validated.

My Solution


using System;
using System.Text;
using System.Text.RegularExpressions;

public static class Kata
{
public static string N00bify(string text)
{
Console.WriteLine(text);
// 딱 봐도 정규표현식을 사용해서 풀어야 되는 문제 어거지로 하면 풀 수 야 있을거 같은데.. 의미가 있나?
// 대소문자 유지 규칙이 있어서 더 더럽다.

// 규칙 1 : "to" and "too" should be replaced by the number 2, even if they are only part of a word (E.g. today = 2day)
string convStr = Regex.Replace(text, "too", "2", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "to", "2", RegexOptions.IgnoreCase);

// 규칙 2 : Likewise, "for" and "fore" should be replaced by the number 4
convStr = Regex.Replace(convStr, "fore", "4", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "for", "4", RegexOptions.IgnoreCase);

// 규칙 3 : Any remaining double o's should be replaced with zeros (E.g. noob = n00b)
convStr = convStr.Replace("Oo", "00").Replace("oo", "00");

// 규칙 4 : "be", "are", "you", "please", "people", "really", "have", and "know" should be changed to "b", "r", "u", "plz", "ppl", "rly", "haz", and "no" respectively (even if they are only part of the word)
convStr = Regex.Replace(convStr, "be", "b", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "are", "r", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "you", "u", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "please", "plz", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "people", "ppl", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "really", "rly", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "have", "haz", RegexOptions.IgnoreCase);
convStr = Regex.Replace(convStr, "know", "no", RegexOptions.IgnoreCase);

// 규칙 5 : When replacing words, always maintain case of the first letter unless another rule forces the word to all caps.

// 규칙 6 : The letter "s" should always be replaced by a "z", maintaining case
convStr = convStr.Replace("s", "z").Replace("S", "Z");


// 규칙 11 : Periods ( . ), commas ( , ), and apostrophes ( ' ) are to be removed
convStr = convStr.Replace(".","").Replace(",","").Replace("'","");

// 규칙 7 : "LOL" must be added to the beginning of any input string starting with a "w" or "W"
if (text[0] == 'w' || text[0] == 'W')
{
convStr = "LOL " + convStr;
}

// 규칙 8 : "OMG" must be added to the beginning (after LOL, if applicable,) of a string 32 characters1 or longer
// Console.WriteLine("convStr: " + convStr + " " + convStr.Length);
int markCount = 0;
for (int i = 0; i < convStr.Length; i++)
{
if (convStr[i] == '!')
{
markCount++;
}
}

int convStrLength = convStr.Length - markCount;
if (convStrLength >= 32)
{
if (convStr.StartsWith("LOL"))
{
convStr = convStr.Replace("LOL", "LOL OMG");
}
else
{
convStr = "OMG " + convStr;
}
}

// 규칙 10 : If the input string starts with "h" or "H", the entire output string should be in ALL CAPS
if (text[0] == 'h' || text[0] == 'H')
{
convStr = convStr.ToUpper();
}
else
{
// 규칙 9 : All evenly numbered words2 must be in ALL CAPS (Example: Cake is very delicious. becomes Cake IZ very DELICIOUZ)
string[] convStrSplits = convStr.Split();
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < convStrSplits.Length; i++)
{
string str = convStrSplits[i];
if (i % 2 != 0)
{
str = str.ToUpper();
}
strBuilder.Append(str);

if (i != convStrSplits.Length - 1)
{
strBuilder.Append(" ");
}
}
convStr = strBuilder.ToString();
}

// 규칙 12 : 3A question mark ( ? ) should have more question marks added to it, equal to the number of words2 in the sentence (Example: Are you a foo? has 4 words, so it would be converted to r U a F00????)
int wordCount = convStr.Split().Length;
StringBuilder sBuilder = new StringBuilder();
for (int i = 0; i < wordCount; i++)
{
sBuilder.Append("?");
}
convStr = convStr.Replace("?", sBuilder.ToString());

sBuilder.Clear();
// 규칙 13 : Similarly, exclamation points ( ! ) should be replaced by a series of alternating exclamation points and the number 1, equal to the number of words2 in the sentence (Example: You are a foo! becomes u R a F00!1!1)
for (int i = 0; i < wordCount; i++)
{
if (i % 2 == 0)
{
sBuilder.Append("!");
}
else
{
sBuilder.Append("1");
}
}
convStr = convStr.Replace("!", sBuilder.ToString());

return convStr;
}
}
  • 일반 문장을 인터넷 용어로 변경하는 문제
  • 문제가 복잡하기 보다는, 조건 자체가 많다.
  • 딱 봐도 정규표현식을 사용해서 풀어야 되는 문제..
  • "to", "too"는 숫자 2로 대체
  • "for", "fore"는 숫자 4로 대체
  • 연속해서 2번 나오는 문자 o의 경우 숫자 0으로 대체
  • "be", "are", "you", "please", "people", "really", "have", "know" 는 각각 "b", "r", "u", "plz", "ppl", "rly", "haz", "no"로 변경한다. (그들이 단어의 일부일지라도)
  • 단어를 대체 할 때 다른 규칙이 단어를 모두 대문자로 사용하지 않는 한 항상 첫 번째 문자의 대 / 소문자를 유지하십시오.
  • 문자 "s"는 항상 "z"로 대치되어야합니다.
  • "LOL"은 "w"또는 "W"로 시작하는 입력 문자열의 시작 부분에 추가되어야합니다.
  • "OMG"는 32 문자 이상의 문자열 시작 부분 (LOL 이후)에 추가해야합니다
  • 짝수 번째 단어의 경우 모두 대문자 여야합니다 (예 : Cake is very delicious. -> Cake IZ very DELICIOUZ)
  • 입력 문자열이 "h"또는 "H"로 시작하면 전체 출력 문자열은 모두 대문자 여야합니다
  • 마침표 (.), 쉼표 (,) 및 아포스트로피 ( ')는 제거됩니다.
  • 물음표 (?)에 더 많은 물음표가 추가되어야합니다 (예 : Are you a foo? 는 4 단어로 구성되어 있으므로 다음과 같이 변경합니다 r U a F00????)
  • 유사하게, 느낌표 (!)는 일련의 대체 느낌표와 문장에서 단어의 수와 같은 숫자로 대체해야합니다 (예 : You are a foo! becomes u R a F00!1!1)

Best Practices

using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using System.Linq;

public static class Kata
{
private static readonly Dictionary<string, string> Replacements = new Dictionary<string, string>
{
{"too", "2"},
{"Too", "2"},
{"to", "2"},
{"To", "2"},
{"be", "b"},
{"Be", "B"},
{"fore", "4"},
{"FORE", "4"},
{"for", "4"},
{"oo", "00"},
{"OO", "00"},
{"Oo", "00"},
{"are", "r"},
{"you", "u"},
{"You", "u"},
{"please", "plz"},
{"people", "ppl"},
{"really", "rly"},
{"have", "haz"},
{"know", "no"},
{"s", "z"},
{"S", "Z"},
{".", ""},
{",", ""},
{"'", ""}
};

private static readonly char[] SentenceDelimiters = {'.', '!', '?'};


public static string N00bify(string text)
{
var result = string.Empty;
var sentences = Regex.Split(text, @"(?<=[.!?])\s+(?=\p{Lt})");
foreach (var sentence in sentences)
{
var currentSentence = sentence;
currentSentence = MapPatterns(currentSentence);
currentSentence = UpperCaseIfStartsWithH(currentSentence);

var lengthBeforeInsertions = currentSentence.Count(c => !SentenceDelimiters.Any(d => d == c));
var needLol = currentSentence.Trim().ToLowerInvariant().StartsWith("w");
var needOmg = lengthBeforeInsertions + (needLol ? 4 : 0) >= 32;
currentSentence = InsertOmgLol(needOmg, currentSentence, needLol);

var wordCount = CountWords(currentSentence);
currentSentence = InsertQuestions(currentSentence, wordCount);
currentSentence = InsertExclamations(currentSentence, wordCount);

var wordsOriginal = currentSentence.Trim().Split(' ');
var wordsModified = wordsOriginal.Select((w, i) => i%2 == 1 ? w.ToUpperInvariant() : w).ToArray();
currentSentence = string.Join(" ", wordsModified);
result += currentSentence;
}
return result;
}

private static string InsertExclamations(string _sentence, int wordCount)
{
return _sentence.Replace("!",
string.Concat(Enumerable.Range(0, wordCount).Select(i => i%2 == 0 ? "!" : "1").ToArray()));
}

private static string InsertQuestions(string _sentence, int wordCount)
{
_sentence = _sentence.Replace("?", new string('?', wordCount));
return _sentence;
}

private static int CountWords(string _sentence)
{
var wordCount = _sentence.Trim().Split(' ').Select(w => !string.IsNullOrWhiteSpace(w)).Count();
return wordCount;
}

private static string InsertOmgLol(bool needOmg, string _sentence, bool needLol)
{
if (needOmg)
_sentence = "OMG " + _sentence;
if (needLol)
_sentence = "LOL " + _sentence;
return _sentence;
}

private static string UpperCaseIfStartsWithH(string _sentence)
{
if (_sentence.Trim().ToLowerInvariant().StartsWith("h"))
_sentence = _sentence.ToUpperInvariant();
return _sentence;
}

private static string MapPatterns(string _sentence)
{
foreach (var replacement in Replacements)
_sentence = _sentence.Replace(replacement.Key, replacement.Value);
return _sentence;
}
}
  • 정규표현식 모르면 거의 못푸는 느낌..
  • 이정도까지 문자열을 변환 시키고 싶으면, 확실히 정규표현식을 알아야 될 듯.

· 약 4분
karais89

Instructions

링크

John and Mary want to travel between a few towns A, B, C ... Mary has on a sheet of paper a list of distances between these towns. ls = [50, 55, 57, 58, 60]. John is tired of driving and he says to Mary that he doesn't want to drive more than t = 174 miles and he will visit only 3 towns.

Which distances, hence which towns, they will choose so that the sum of the distances is the biggest possible

to please Mary and John- ? Example:

With list ls and 3 towns to visit they can make a choice between: [50,55,57],[50,55,58],[50,55,60],[50,57,58],[50,57,60],[50,58,60],[55,57,58],[55,57,60],[55,58,60],[57,58,60].

The sums of distances are then: 162, 163, 165, 165, 167, 168, 170, 172, 173, 175.

The biggest possible sum taking a limit of 174 into account is then 173 and the distances of the 3 corresponding towns is [55, 58, 60].

The function chooseBestSum (or choose_best_sum or ... depending on the language) will take as parameters t (maximum sum of distances, integer >= 0), k (number of towns to visit, k >= 1) and ls (list of distances, all distances are positive or null integers and this list has at least one element). The function returns the "best" sum ie the biggest possible sum of k distances less than or equal to the given limit t, if that sum exists, or otherwise nil, null, None, Nothing, depending on the language. With C++, C, Rust, Swift, Go, Kotlin return -1.

Examples:

ts = [50, 55, 56, 57, 58] choose_best_sum(163, 3, ts) -> 163

xs = [50] choose_best_sum(163, 3, xs) -> nil (or null or ... or -1 (C++, C, Rust, Swift, Go)

ys = [91, 74, 73, 85, 73, 81, 87] choose_best_sum(230, 3, ys) -> 228

My Solution


using System;
using System.Collections.Generic;
using System.Linq;

public static class SumOfK
{
// https://stackoverflow.com/questions/33336540/how-to-use-linq-to-find-all-combinations-of-n-items-from-a-set-of-numbers
public static IEnumerable<IEnumerable<T>> DifferentCombinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).DifferentCombinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}

public static int? chooseBestSum(int t, int k, List<int> ls)
{
if (ls == null || ls.Count < k)
{
return null;
}

var sumlists = DifferentCombinations(ls, k);
int bestSum = 0;
foreach(var list in sumlists)
{
int sum = 0;
foreach (var l in list)
{
sum += l;
}

if (bestSum <= sum && sum <= t)
{
bestSum = sum;
}
}

if (bestSum == 0)
{
return null;
}

return bestSum;
}
}
  • 존은 오직 3개의 도시만 방문할 것이고, 174 마일 보다 더 달리지 않을 것이다.
  • 거리의 합이 가장 큰 것을 선택 할 것이다.
  • t = 최대 거리 합계
  • k = 방문 가능한 도시의 수
  • ls = 방문할 도시의 거리가 적혀 있는 리스트
  • m개중 n개를 선택하는 방법?
  • 조합을 구하는 문제인 듯
  • 조합 구하는 알고리즘은 스택 오버플로우에서 참고 했다.

Best Practices

using System.Collections.Generic;
using System.Linq;

public static class SumOfK
{
public static int? chooseBestSum(int t, int k, List<int> ls) =>
ls.Combinations(k)
.Select(c => (int?) c.Sum())
.Where(sum => sum <= t)
.DefaultIfEmpty()
.Max();

// Inspired by http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n
public static IEnumerable<IEnumerable<int>> Combinations(this IEnumerable<int> ls, int k) =>
k == 0 ? new[] { new int[0] } :
ls.SelectMany((e, i) =>
ls.Skip(i + 1)
.Combinations(k - 1)
.Select(c => (new[] {e}).Concat(c)));
}

여기도 마찬가지로 조합 구하는 알고리즘은 스택 오버 플로우에서 참조 한듯.

· 약 4분
karais89

Instructions

링크

We need to encode a message.

Only alpha characters will be encoded. Non-alpha characters will be copied. So, the message " 0123. " would be encoded to " 0123. ".

For alpha characters, we will follow these rulse:

1. All alpha characters will be treated as upper case
2. The first alpha character will not change (except for switching to upper case).
3. All subsequent alpha characters will be shifted toward 'Z' by the
alphabetical position of the previous alpha character.
(wrap back to 'A' if 'Z' is passed)

For example: "He1lo" would be encoded as follows:

H -> H (first alpha character does not change)
e -> M (H is the previous alpha character, and is the 8th letter in the alphabet. E + 8 = M)
1 -> 1 (non alpha characters do not change)
l -> Q (E is the previous alpha character, and is the 5th letter in the alphabet. L + 5 = Q)
o -> A (L is the previous alpha character, and is the 12th letter in the alphabet. O + 12 = A)

So, "He1lo" would be encoded to "HM1QA"

Write two functions. One to encode and one to decode. (Decoding "HM1QA" should yield "HE1LO")

For both functions, empty strings and null strings should return empty strings.

My Solution

using System;
using System.Text;

namespace Kata
{
public class PaulCipher
{
public static int GetCharOrder(char ch)
{
return (ch - 'A') + 1;
}

public static char MoveCharOrder(char ch, int order)
{
char moveCh = (char)(ch + order);
if (moveCh > 'Z')
{
int reOrder = moveCh - 'Z';
return (char)('A' + reOrder - 1);
}

if (moveCh < 'A')
{
int reOrder = 'A' - moveCh;
return (char)('Z' - reOrder + 1);
}

return moveCh;
}

public static string Encode(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

StringBuilder builder = new StringBuilder();
string upStr = input.ToUpper();
char prevCh = (char)0;
for (int i = 0; i < upStr.Length; i++)
{
char ch = upStr[i];
if (char.IsLetter(ch))
{
if (prevCh != (char)0)
{
int order = GetCharOrder(prevCh);
prevCh = ch;
ch = MoveCharOrder(ch, order);
}
else
{
prevCh = ch;
}
}
builder.Append(ch);
}
return builder.ToString();
}

public static string Decode(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

StringBuilder builder = new StringBuilder();
string upStr = input.ToUpper();
char prevCh = (char)0;
for (int i = 0; i < upStr.Length; i++)
{
char ch = upStr[i];
if (char.IsLetter(ch))
{
if (prevCh != (char)0)
{
int order = GetCharOrder(prevCh);
ch = MoveCharOrder(ch, -order);
}

prevCh = ch;
}
builder.Append(ch);
}
return builder.ToString();
}
}
}

  • 알파벳 문자만 인코딩 되고, 나머지는 그대로 복사 된다.
  • 모든 알파벳은 대문자로 처리 된다.
  • 첫번째 알파벳은 바뀌지 않는다 (대문자로 처리되는것을 제외하고)
  • 변경되는 알파벳은 이전 알파벳의 순서 만큼 더해줘서 변경된다.
  • 해당 기능을 가지는 디코더와 인코더를 만들어라.

하나의 함수 안에 처리를 하려다가, 그냥 제출 버튼을 눌렀다. 이전 문자 저장하는 순서가 살짝 달라서 조금 애먹었다.

Best Practices

namespace Kata
{
public class PaulCipher
{
static string Process(string input, int direction)
{
if (string.IsNullOrEmpty(input)) return "";
var cc = input.ToUpper().ToCharArray();
var prevA = (char)0;
for (var i = 0; i < cc.Length; i++)
{
if (!char.IsLetter(cc[i])) continue;
if (prevA == 0) prevA = cc[i];
else
{
var c = cc[i] + direction*(prevA - 64);
if (c > 'Z' || c < 'A') c = c + -direction*('Z' - 64);
prevA = direction > 0 ? cc[i] : (char)c;
cc[i] = (char)c;
}
}
return new string(cc);
}

public static string Encode(string input)
{
return Process(input, 1);
}

public static string Decode(string input)
{
return Process(input, -1);
}
}
}

이 해결책에서는 함수 하나에 처리 하였다. 문자 저장하는 순서도 디코더에서는 변경되기 전 문자를 저장 인코더에서는 변경된 문자를 저장한다.

· 약 3분
karais89

Instructions

링크

Description: Task Write a function deNico/de_nico() that accepts two parameters:

  • key/$key - string consists of unique letters and digits
  • message/$message - string with encoded message and decodes the message using the key.

First create a numeric key basing on the provided key by assigning each letter position in which it is located after setting the letters from key in an alphabetical order.

For example, for the key crazy we will get 23154 because of acryz (sorted letters from the key). Let's decode cseerntiofarmit on using our crazy key.

1 2 3 4 5
---------
c s e e r
n t i o f
a r m i t
o n

After using the key:

2 3 1 5 4
---------
s e c r e
t i n f o
r m a t i
o n

Notes

  • The message is never shorter than the key.
  • Don't forget to remove trailing whitespace after decoding the message

Examples

deNico("crazy", "cseerntiofarmit on ") => "secretinformation"
deNico("abc", "abcd") => "abcd"
deNico("ba", "2143658709") => "1234567890"
deNico("key", "eky") => "key"
Check the test cases for more examples.

Related Kata Basic Nico - encode

My Solution

using System;
using System.Text;
using System.Collections.Generic;

public class Kata {

public static List<int> GetOrdersByKey(string key)
{
char[] charKeys = key.ToCharArray();
List<char> sortCharKeys = new List<char>(charKeys);
sortCharKeys.Sort();

List<int> orders = new List<int>();
for (int i = 0; i < charKeys.Length; i++)
{
for (int j = 0; j < sortCharKeys.Count; j++)
{
if (charKeys[i] == sortCharKeys[j])
{
if (!orders.Contains(j))
{
orders.Add(j);
}
}
}
}

return orders;
}

public static string ConvMessageToKey(List<int> orders, string message)
{
if (orders == null || orders.Count == 0)
{
return message;
}

int subLength = orders.Count;
StringBuilder strBuilder = new StringBuilder();
StringBuilder subStrBuilder = new StringBuilder();
for (int i = 0; i < message.Length; i += subLength)
{
string subStr = (i + subLength < message.Length) ? (message.Substring(i, subLength)) : (message.Substring(i));
subStrBuilder.Clear();
for (int j = 0; j < orders.Count; j++)
{
int idx = orders[j];
if (idx < subStr.Length)
{
subStrBuilder.Append(subStr[idx]);
}
}
strBuilder.Append(subStrBuilder.ToString().Trim());
}

return strBuilder.ToString();
}

public static string DeNico(string key, string message)
{
// step 1 : key string sorting number
List<int> orders = GetOrdersByKey(key);

// step 2: message convert orders
string convMsg = ConvMessageToKey(orders, message);

return convMsg;
}
}
  • 주어진 key에 대해서 알파벳 순서로 정렬한 key를 구하고 해당 인덱스에 각 key 값의 인덱스를 대입하여 새로운 int형 key를 만든다.
  • 이 int형 리스트를 사용하여 주어진 message를 변환해준다.

이 문제를 풀고 5 kyu가 되었다.

Best Practices

using System.Linq;
using System;

public class Kata {
public static string DeNico(string key, string m) {
int [] coder = key.OrderBy(x=>x).Select(e=> key.IndexOf(e)).ToArray();
return string.Concat(m.Select((e,i)=>m[ ((int)(i/ key.Length))*key.Length + Array.IndexOf(coder,i%key.Length)])).TrimEnd(' ');;
}
}

이걸 보면 Linq를 배워야 되겠다 2줄이면 해결이 되네..

  • GetOrdersByKey 함수는 int [] coder = key.OrderBy(x=>x).Select(e=> key.IndexOf(e)).ToArray(); 이렇게 한줄이면 해결이 된다.
  • ConvMessageToKey 함수는 string.Concat(m.Select((e,i)=>m[ ((int)(i/ key.Length))*key.Length + Array.IndexOf(coder,i%key.Length)])).TrimEnd(' '); 이거 한줄이면 해결이 된다.

· 약 4분
karais89

Instructions

링크

Introduction

  Welcome Adventurer. Your aim is to navigate the maze and reach the finish point without touching any walls. Doing so will kill you instantly!

Maze Runner Task

  You will be given a 2D array of the maze and an array of directions. Your task is to follow the directions given. If you reach the end point before all your moves have gone, you should return Finish. If you hit any walls or go outside the maze border, you should return Dead. If you find yourself still in the maze after using all the moves, you should return Lost.

The Maze array will look like

maze = [[1,1,1,1,1,1,1],
[1,0,0,0,0,0,3],
[1,0,1,0,1,0,1],
[0,0,1,0,0,0,1],
[1,0,1,0,1,0,1],
[1,0,0,0,0,0,1],
[1,2,1,0,1,0,1]]

..with the following key

0 = Safe place to walk
1 = Wall
2 = Start Point
3 = Finish Point
  direction = ["N","N","N","N","N","E","E","E","E","E"] == "Finish"

Rules

  1. The Maze array will always be square i.e. N x N but its size and content will alter from test to test.
2. The start and finish positions will change for the final tests.
3. The directions array will always be in upper case and will be in the format of N = North, E = East, W = West and S = South.

Good luck, and stay safe!

Kata Series

If you enjoyed this, then please try one of my other Katas. Any feedback, translations and grading of beta Katas are greatly appreciated. Thank you.

My Solution

using System;

namespace CodeWars
{
class Kata
{
private enum MazeState
{
SafePlaceToWalk,
Wall,
StartPoint,
EndPoint
};

public struct Point
{
public int x;
public int y;

public override string ToString()
{
return $"[{x}, {y}]";
}
}

public Point FindStartPoint(int[,] maze)
{
for (int i = 0; i < maze.GetLength(0); i++)
{
for (int j = 0; j < maze.GetLength(1); j++)
{
if (maze[i,j] == (int)MazeState.StartPoint)
{
Point p = new Point();
p.y = i;
p.x = j;
return p;
}
}
}

Point notFoundPoint = new Point();
notFoundPoint.x = -1;
notFoundPoint.y = -1;
return notFoundPoint;
}

public string mazeRunner(int[,] maze, string[] directions)
{
// Code here
Point startPoint = FindStartPoint(maze);
Point nextPoint = startPoint;

for (int i = 0; i < directions.Length; i++)
{
switch(directions[i])
{
case "N": // up
nextPoint.y--;
break;
case "S": // down
nextPoint.y++;
break;
case "E": // right
nextPoint.x++;
break;
case "W": // left
nextPoint.x--;
break;
}

// out side check
if (nextPoint.x < 0 || nextPoint.x >= maze.GetLength(0) ||
nextPoint.y < 0 || nextPoint.y >= maze.GetLength(1))
{
return "Dead";
}

switch ((MazeState)maze[nextPoint.y, nextPoint.x])
{
case MazeState.Wall:
return "Dead";
case MazeState.EndPoint:
return "Finish";
default:
// nothing
break;
}
}

return "Lost";
}
}
}
  • direction 배열에 있는 스트링 값으로 start point 부터 시작하여 진행.
  • 중간에 벽에 닿으면 Dead 리턴
  • 모든 행동이 끝나기전에 finish에 도착한다면 Finish 리턴
  • 모든 행동이 끝나도 죽지 않았거나, finish 지점에 도착 하지 못했다면 Lost 리턴

Best Practices

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeWars
{
class Kata
{
public string mazeRunner(int[,] maze, string[] directions)
{
int startX = 0;
int startY = 0;
double len = Math.Sqrt(maze.Length);
for (int x = 0; x < len; x++)
{
for (int y = 0; y < len; y++)
{
if (maze[y, x] == 2) { startX = x; startY = y; }
}
}
for (int x = 0; x < directions.Length; x++)
{
switch (directions[x])
{
case "N": startY -= 1; break;
case "E": startX += 1; break;
case "S": startY += 1; break;
case "W": startX -= 1; break;
}
if (startY < 0 || startY > len - 1 || startX < 0 || startX > len - 1 || maze[startY, startX] == 1) { return "Dead"; }
if (maze[startY,startX] == 3) { return "Finish"; }
}

return "Lost";
}
}
}
  • 논리는 거의 비슷해 보인다.
  • 예외 처리 및 매직 넘버를 사용하는 방식이라 코드가 더 짧다.
  • 이게 더 베스트한 방식인지는 의문이 든다.