# Lineのコーディングスキルテスト2021をやってみた.
噂に聞いていた実装編をやってみました.
https://engineering.linecorp.com/ja/blog/commentary-of-coding-test-2021/
制限時間3時間で, A4 4枚程度の使用を満たすコンソールアプリを作れという話でタクシーの料金計算を実装しました.
せっかくなので実装を貼っちゃいましょう with Golang.
:::spoiler 実装 using go
```go
package main
import (
"bufio"
"errors"
"fmt"
"os"
"regexp"
"strconv"
)
type Time struct {
Hh int
Mm int
Ss int
Fff int
}
func (t1 *Time) GetMs() int {
return (t1.Hh*3600+t1.Mm*60+t1.Ss)*1000 + t1.Fff
}
type Meter struct {
Integer int
Decimal int
}
func (m Meter) Get10xMeter() int {
return m.Decimal*10 + m.Decimal
}
type Record struct {
Time
Meter
}
func (r1 *Record) IsLaterThan(r2 *Record) bool {
return r1.Time.GetMs() > r2.Time.GetMs()
}
func (r1 *Record) IsIn45sAfter(r2 *Record) bool {
return r1.Time.GetMs()-r2.Time.GetMs() <= 45*1000
}
func (now *Record) IsSlow(prev *Record) bool {
KmPerH := (now.Get10xMeter()) / 10 / (now.Time.GetMs() - prev.Time.GetMs()) * 3600
return KmPerH <= 10
}
func (now *Record) IsNight() bool {
t := now.Time.GetMs() % 24 * 60 * 60 * 1000
midnight := (&Time{}).GetMs()
sixAM := (&Time{Hh: 6}).GetMs()
return (midnight <= t && t < sixAM)
}
func (now *Record) IsPeakTime() bool {
t := now.Time.GetMs() % 24 * 60 * 60 * 1000
sixAM := (&Time{Hh: 6}).GetMs()
nine30AM := (&Time{Hh: 9, Mm: 30}).GetMs()
six30PM := (&Time{Hh: 18, Mm: 30}).GetMs()
midnight := (&Time{Hh: 23, Mm: 59, Ss: 59, Fff: 999}).GetMs()
return (sixAM <= t && t < nine30AM) || (six30PM < t && t < midnight)
}
// 走行レコード
func ParseRunRecord(s string) (*Record, error) {
regex := regexp.MustCompile(`^(\d\d):(\d\d):(\d\d)\.(\d\d\d)\s(\d\d?)\.(\d)$`)
if !regex.MatchString(s) {
return nil, fmt.Errorf("%v does not match with %v", s, regex)
}
matches := regex.FindStringSubmatch(s)
hh, _ := strconv.ParseInt(matches[0], 10, 64)
mm, _ := strconv.ParseInt(matches[1], 10, 64)
ss, _ := strconv.ParseInt(matches[2], 10, 64)
fff, _ := strconv.ParseInt(matches[3], 10, 64)
meterInt, _ := strconv.ParseInt(matches[4], 10, 64)
meterDecimal, _ := strconv.ParseInt(matches[5], 10, 64)
if hh > 99 {
return nil, errors.New("hh > 99")
}
r := Record{
Time{
Hh: int(hh),
Mm: int(mm),
Ss: int(ss),
Fff: int(fff),
},
Meter{
Integer: int(meterInt),
Decimal: int(meterDecimal),
},
}
return &r, nil
}
type Records []Record
func (rs Records) Validate() error {
if len(rs) < 2 {
return errors.New("records must contain at least 2 lines")
}
for i := 0; i < len(rs)-1; i++ {
if ok := rs[i+1].IsLaterThan(&rs[i]) && rs[i+1].IsIn45sAfter(&rs[i]); !ok {
return errors.New("time of records must ascending order")
}
}
totalMeterX10 := 0
for i := range rs {
totalMeterX10 += 10*(rs[i].Meter.Integer) + rs[i].Meter.Decimal
}
if totalMeterX10 < 1 {
return errors.New("total meter must be at least 0.1")
}
return nil
}
func (rs Records) GetFirstFare() int {
if rs[0].IsNight() {
return 600
}
if rs[0].IsPeakTime() {
return 520
}
return 400
}
func (r Record) GetKukanFare() int {
switch {
case r.IsNight():
return 60
case r.IsPeakTime():
return 52
default:
return 40
}
}
func Min(x int, y int) int {
if x < y {
return x
}
return y
}
func (rs Records) CalcFare() int {
fare := 0
short := 0
long := 0
m10x := 0
for _, r := range rs {
m10x += r.Meter.Get10xMeter()
if m10x >= (1000+400*short)*10 && m10x < 10200 {
short += 1
fare += r.GetKukanFare()
}
if m10x >= (10200+350*long)*10 {
long += 1
fare += r.GetKukanFare()
}
}
slowTaxiMeter10x := 0
for i := 0; i < len(rs)-1; i++ {
if rs[i+1].IsSlow(&rs[i]) {
slowTaxiMeter10x += rs[i+1].Get10xMeter() - rs[i].Get10xMeter()
}
}
slowTaxiMeter10x /= 10
fare /= 10
return rs.GetFirstFare() + fare
}
func main() {
filename := "input.txt"
fp, err := os.Open(filename)
if err != nil {
panic(err)
}
defer fp.Close()
scanner := bufio.NewScanner(fp)
var records Records
for scanner.Scan() {
record, err := ParseRunRecord(scanner.Text())
if err != nil {
panic(err)
}
records = append(records, *record)
}
if err := records.Validate(); err != nil {
panic(err)
}
fmt.Print(records.CalcFare())
}
```
:::
## 良かった点
気をつけられた点としては, できるだけ少数ではなくintでそれぞれの値を扱って来たこと(SecondではなくMicroSecondをintで持っとくとか).
## ダメだった点
深夜に思い立ってゆっくりやっていた, 仕様の一部は満たせていません(泣). (低速のやつ)
コーディングと言うより, 仕様とのにらめっこゲームになっていました...
今回はテストケースが手もとにない, 探り探りの状況でひとまず仕様を満たすものができて, ネットから簡単なテストケースが動いただけです.
正直テストのテの字もできていないので, 全然点数をもらえない気はします.
## どうすればよかったか
1. 先に問題全体に目を通して
2. 大体の仕様を把握した上で, テストが1つ通るようなコードを書く
3. その上で, 細かい実装を処理していく.
(できればテストコードを書きながら...)
## これからやること
正直, インターンの定員30人というのはヤバすぎるので, 相当コーディング試験で頑張らないといけないなぁという気持ちです.
ですが, 僕は競技プログラミング的な強さはあまりないので, こういうところを練習していけたらと思います(アレ?)