diff --git a/.gitignore b/.gitignore
index d9c7f8a33ae807b7d0aacd54af2c9152680bb2b6..c05959c07590c12ab22d5188ee294faa5d2c5ef2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
 newrelic_agent.log
+.vscode/configurationCache.log
+.vscode/dryrun.log
+.vscode/settings.json
+.vscode/targets.log
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9adaa975f9b7ecb4e1d9a5cf6bf5960a2f9a5147
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,16 @@
+image: golang:alpine
+
+before_script:
+  - export GOPATH="$CI_PROJECT_DIR/.cache"
+
+stages:
+  - prepare
+  - test_day1
+  - test_day2
+  - test_day3
+  - test_day4
+
+include:
+  # local: ci/*.yml
+  - local: ci/.01-prepare_app.yml
+  - local: ci/.02-test_app.yml
diff --git a/README.md b/README.md
index 6742ed112531071692cfe4997e5b492c9db3acb2..db982836c83949426470b3b4c00643f83433d8a4 100644
--- a/README.md
+++ b/README.md
@@ -37,7 +37,7 @@
 
     # or
 
-    go run ./day3/car.go ./day3/main.go -c 10 -d 150
+    go run ./day3/main.go -c 10 -d 150
   ```
 
 - Test
@@ -55,7 +55,7 @@
 
     # or
 
-    go run ./day4/race.go ./day4/car.go ./day4/main.go -c 15 -d 150 -w 5
+    go run ./day4/main.go -c 15 -d 150 -w 5
   ```
 
 - Test
diff --git a/ci/.01-prepare_app.yml b/ci/.01-prepare_app.yml
new file mode 100644
index 0000000000000000000000000000000000000000..26933928675fcd017e8b6808011619a55d0ccd26
--- /dev/null
+++ b/ci/.01-prepare_app.yml
@@ -0,0 +1,12 @@
+prepare_app:
+  stage: prepare
+  cache:
+    paths:
+      - .cache
+  script:
+    - apk add build-base make bash git
+    - mkdir -p .cache
+  only:
+    - merge_requests
+    - dev
+    - master
diff --git a/ci/.02-test_app.yml b/ci/.02-test_app.yml
new file mode 100644
index 0000000000000000000000000000000000000000..81ac2b7fcfb07a2f84d4cd772e9c40f2b4d874c6
--- /dev/null
+++ b/ci/.02-test_app.yml
@@ -0,0 +1,43 @@
+test_day1:
+  stage: test_day1
+  script:
+    - apk add build-base make bash git
+    - go mod tidy
+    - make day1.test
+  only:
+    - merge_requests
+    - dev
+    - master
+
+test_day2:
+  stage: test_day2
+  script:
+    - apk add build-base make bash git
+    - go mod tidy
+    - make day2.test
+  only:
+    - merge_requests
+    - dev
+    - master
+
+test_day3:
+  stage: test_day3
+  script:
+    - apk add build-base make bash git
+    - go mod tidy
+    - make day3.test
+  only:
+    - merge_requests
+    - dev
+    - master
+
+test_day4:
+  stage: test_day4
+  script:
+    - apk add build-base make bash git
+    - go mod tidy
+    - make day4.test
+  only:
+    - merge_requests
+    - dev
+    - master
diff --git a/day1/vars.go b/day1/lib/vars.go
similarity index 93%
rename from day1/vars.go
rename to day1/lib/vars.go
index 22b2bcc1fefbbd1597451173c8b155d88b0f147c..e78bfffb72d1771870e7e8dbd5f175bc92917ab7 100644
--- a/day1/vars.go
+++ b/day1/lib/vars.go
@@ -1,4 +1,4 @@
-package main
+package lib
 
 import "github.com/eaciit/toolkit"
 
diff --git a/day1/vars_test.go b/day1/lib/vars_test.go
similarity index 95%
rename from day1/vars_test.go
rename to day1/lib/vars_test.go
index 4bee125038fc04deb126b6d7637318f4b76524d9..ce57f074940771d82ca64771e3ac5212ab9a822c 100644
--- a/day1/vars_test.go
+++ b/day1/lib/vars_test.go
@@ -1,11 +1,11 @@
-package main_test
+package lib_test
 
 import (
 	"testing"
 
 	// "github.com/stretchr/testify/assert"
 
-	. "go-sinau/day1"
+	. "go-sinau/day1/lib"
 
 	"github.com/stretchr/testify/assert"
 )
diff --git a/day1/main.go b/day1/main.go
index f3bdffcccd35507210a22531b7d00b703de7d906..1cece2966df36ac235005d6c160c348aaa262be4 100644
--- a/day1/main.go
+++ b/day1/main.go
@@ -1,6 +1,9 @@
 package main
 
-import "fmt"
+import (
+	"fmt"
+	. "go-sinau/day1/lib"
+)
 
 var (
 	f32 float32
diff --git a/day2/word.go b/day2/lib/word.go
similarity index 99%
rename from day2/word.go
rename to day2/lib/word.go
index 819c93304801a889eca2ec3a4d68911d46dd93c9..432573c53e2ae1c8d229d0fa7b140b7e9ada6749 100644
--- a/day2/word.go
+++ b/day2/lib/word.go
@@ -1,4 +1,4 @@
-package main
+package lib
 
 import (
 	"math"
diff --git a/day2/word_test.go b/day2/lib/word_test.go
similarity index 98%
rename from day2/word_test.go
rename to day2/lib/word_test.go
index 1ac800ce4ff064e3420f929e08aab92aa5be5ed2..45ab2e54b6709eec27454015e03b1ecd236a2291 100644
--- a/day2/word_test.go
+++ b/day2/lib/word_test.go
@@ -1,7 +1,7 @@
-package main_test
+package lib_test
 
 import (
-	. "go-sinau/day2"
+	. "go-sinau/day2/lib"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
diff --git a/day2/main.go b/day2/main.go
index edabc4cfd79aa2bb11db3d07f526af818ac4a07d..ade08d615e218144d499836ccf4906a975788ab4 100644
--- a/day2/main.go
+++ b/day2/main.go
@@ -1,6 +1,9 @@
 package main
 
-import "fmt"
+import (
+	"fmt"
+	. "go-sinau/day2/lib"
+)
 
 func main() {
 	txt := []string{"Hello", "World"}
diff --git a/day3/car.go b/day3/lib/car.go
similarity index 99%
rename from day3/car.go
rename to day3/lib/car.go
index 29420d09c4449b70dd339b456794875a07562457..4a330397c5bc83085839dbfa70e15f47ff57ed41 100644
--- a/day3/car.go
+++ b/day3/lib/car.go
@@ -1,4 +1,4 @@
-package main
+package lib
 
 import (
 	"fmt"
diff --git a/day3/car_test.go b/day3/lib/car_test.go
similarity index 98%
rename from day3/car_test.go
rename to day3/lib/car_test.go
index c44d59b8ab26de38823d38acfa13507c71590c10..1cd53faf49944606215bdf74702b37a048051375 100644
--- a/day3/car_test.go
+++ b/day3/lib/car_test.go
@@ -1,7 +1,7 @@
-package main_test
+package lib_test
 
 import (
-	. "go-sinau/day3"
+	. "go-sinau/day3/lib"
 	"testing"
 
 	"github.com/logrusorgru/aurora"
diff --git a/day3/main.go b/day3/main.go
index 9d23c567069e36803312b80775cd166079d5da02..1d3c58b71717ab9b4316bb4e7d3f8ec9ba1558e6 100644
--- a/day3/main.go
+++ b/day3/main.go
@@ -3,6 +3,7 @@ package main
 import (
 	"flag"
 	"fmt"
+	. "go-sinau/day3/lib"
 	"time"
 
 	"github.com/logrusorgru/aurora"
diff --git a/day4/car.go b/day4/car.go
deleted file mode 100644
index b28eeb77e69c33ba99f3b9091b12f62950db9ed4..0000000000000000000000000000000000000000
--- a/day4/car.go
+++ /dev/null
@@ -1,116 +0,0 @@
-package main
-
-import (
-	"context"
-	"fmt"
-	"strings"
-	"sync"
-	"time"
-
-	"github.com/eaciit/toolkit"
-	"github.com/logrusorgru/aurora"
-)
-
-type Car struct {
-	ID    string
-	Y     int
-	Lapse int
-	Name  string
-
-	Color func(interface{}) aurora.Value
-}
-
-func Print(c *Car, distance int, isCar bool) {
-	car := "|=" + c.ID + "->"
-	carTxt := strings.Repeat(" ", c.Lapse) + car
-
-	if len(carTxt) < distance {
-		if isCar {
-			carTxt = "|" + strings.Repeat(" ", distance-len(carTxt)) + carTxt
-		} else {
-			carTxt = "|" + strings.Repeat(" ", distance) + "|"
-		}
-	}
-
-	fmt.Printf("\033[%d;%dH", c.Y, 1)
-	fmt.Println(c.Color(carTxt))
-}
-
-func Race(ctx context.Context, wg *sync.WaitGroup, mtx *sync.Mutex, c *Car, distance int, cwon int, raceDetails *RaceDetails, cancel context.CancelFunc) {
-	defer wg.Done()
-
-	for i := 0; i <= distance; i++ {
-		// stop the race
-		if len(raceDetails.Winners) >= cwon {
-			cancel()
-			break
-		}
-
-		randInt := toolkit.RandInt(300)
-		times := time.Duration(randInt) * time.Millisecond
-		time.Sleep(times)
-		Print(c, i, true)
-
-		// prevent race condition
-		mtx.Lock()
-
-		details := raceDetails.Details
-
-		if val, ok := details[c.ID]; ok {
-			val.Times = append(val.Times, randInt)
-		} else {
-			details[c.ID] = &RaceDetail{
-				ID:    c.ID,
-				Times: []int{randInt},
-			}
-		}
-
-		if i == distance {
-			raceDetails.Winners = append(raceDetails.Winners, c.ID)
-			details[c.ID].SetOrder(len(raceDetails.Winners))
-		}
-
-		mtx.Unlock()
-	}
-}
-
-func PrepareCar(n int, cars *([]*Car)) {
-	*cars = make([]*Car, n)
-
-	for i := 0; i < n; i++ {
-		car := new(Car)
-		car.ID = fmt.Sprintf("CAR-%d", i+1)
-		car.Lapse = 0
-		car.Y = i + 1
-		car.Name = toolkit.RandomString(4)
-
-		switch i % 8 {
-		case 0:
-			car.Color = aurora.Red
-
-		case 1:
-			car.Color = aurora.Yellow
-
-		case 2:
-			car.Color = aurora.Green
-
-		case 3:
-			car.Color = aurora.Cyan
-
-		case 4:
-			car.Color = aurora.White
-
-		case 5:
-			car.Color = aurora.Blue
-
-		case 6:
-			car.Color = aurora.Magenta
-
-		case 7:
-			car.Color = aurora.Red
-
-		}
-
-		(*cars)[i] = car
-	}
-}
diff --git a/day4/car_test.go b/day4/car_test.go
deleted file mode 100644
index ffc36bfccbf52436b97976470dc63e0e0371e121..0000000000000000000000000000000000000000
--- a/day4/car_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package main_test
-
-import (
-	. "go-sinau/day4"
-	"testing"
-
-	"github.com/logrusorgru/aurora"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestPrint(t *testing.T) {
-	type Payload struct {
-		Car      Car
-		Distance int
-		IsCar    bool
-	}
-
-	tt := []struct {
-		Name     string
-		Payload  Payload
-		Expected interface{}
-	}{
-		{
-			Name: "Will print winner car",
-			Payload: Payload{
-				Car: Car{
-					ID:    "CAR-1",
-					Y:     1,
-					Lapse: 1,
-					Name:  "John Doe",
-					Color: aurora.Red,
-				},
-				Distance: 100,
-				IsCar:    true,
-			},
-		},
-		{
-			Name: "Will print the pipe",
-			Payload: Payload{
-				Car: Car{
-					ID:    "CAR-1",
-					Y:     1,
-					Lapse: 1,
-					Name:  "John Doe",
-					Color: aurora.Red,
-				},
-				Distance: 100,
-				IsCar:    false,
-			},
-		},
-	}
-
-	for _, test := range tt {
-		t.Run(test.Name, func(t *testing.T) {
-			payload := test.Payload
-			Print(&payload.Car, payload.Distance, payload.IsCar)
-		})
-	}
-}
-
-// func TestRace(t *testing.T) {
-// 	type Payload struct {
-// 		Car      Car
-// 		Distance int
-// 		ChanWon  chan string
-// 	}
-
-// 	tt := []struct {
-// 		Name     string
-// 		Payload  Payload
-// 		Expected interface{}
-// 	}{
-// 		{
-// 			Name: "Winner will save to channel",
-// 			Payload: Payload{
-// 				Car: Car{
-// 					ID:    "CAR-1",
-// 					Y:     1,
-// 					Lapse: 1,
-// 					Name:  "John Doe",
-// 					Color: aurora.Red,
-// 				},
-// 				Distance: 10,
-// 				ChanWon:  make(chan string),
-// 			},
-// 		},
-// 	}
-
-// 	for _, test := range tt {
-// 		t.Run(test.Name, func(t *testing.T) {
-// 			payload := test.Payload
-// 			go Race(&payload.Car, payload.Distance, payload.ChanWon)
-// 			winner := <-payload.ChanWon
-// 			assert.NotEmpty(t, winner)
-// 			assert.Equal(t, winner, payload.Car.ID)
-// 		})
-// 	}
-// }
-
-func TestPrepareCar(t *testing.T) {
-	type Cars []*Car
-	type Payload struct {
-		Count int
-		Cars  Cars
-	}
-
-	tt := []struct {
-		Name     string
-		Payload  Payload
-		Expected int
-	}{
-		{
-			Name: "Make 10 cars",
-			Payload: Payload{
-				Count: 10,
-				Cars:  Cars{},
-			},
-			Expected: 10,
-		},
-	}
-
-	for _, test := range tt {
-		t.Run(test.Name, func(t *testing.T) {
-			payload := &test.Payload
-			PrepareCar(payload.Count, (*[]*Car)(&payload.Cars))
-			assert.NotEmpty(t, payload.Cars)
-			assert.Equal(t, len(payload.Cars), test.Expected)
-		})
-	}
-}
diff --git a/day4/lib/car.go b/day4/lib/car.go
new file mode 100644
index 0000000000000000000000000000000000000000..1ba57db1f792b690ad5a85462c9104305f30276f
--- /dev/null
+++ b/day4/lib/car.go
@@ -0,0 +1,76 @@
+package lib
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/eaciit/toolkit"
+	"github.com/logrusorgru/aurora"
+)
+
+type Car struct {
+	ID    string
+	Y     int
+	Lapse int
+	Name  string
+
+	Color func(interface{}) aurora.Value
+
+	trip       int
+	times      []time.Duration
+	totalTimes time.Duration
+}
+
+func (c *Car) Trip() int {
+	return c.trip
+}
+
+func (c *Car) Times() []time.Duration {
+	return c.times
+}
+
+func (c *Car) TotalTimes() time.Duration {
+	return c.totalTimes
+}
+
+func (c *Car) Speed() float64 {
+	return float64(c.trip) / c.totalTimes.Seconds()
+}
+
+func PrepareCar(n int, cars *([]*Car)) {
+	*cars = make([]*Car, n)
+
+	for i := 0; i < n; i++ {
+		car := new(Car)
+		car.ID = fmt.Sprintf("CAR-%03d", i+1)
+		car.Lapse = 0
+		car.Y = i + 1
+		car.Name = toolkit.RandomString(4)
+
+		switch i % 7 {
+		case 0:
+			car.Color = aurora.Red
+
+		case 1:
+			car.Color = aurora.Yellow
+
+		case 2:
+			car.Color = aurora.Green
+
+		case 3:
+			car.Color = aurora.Cyan
+
+		case 4:
+			car.Color = aurora.White
+
+		case 5:
+			car.Color = aurora.Blue
+
+		case 6:
+			car.Color = aurora.Magenta
+
+		}
+
+		(*cars)[i] = car
+	}
+}
diff --git a/day4/lib/car_test.go b/day4/lib/car_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..67f4a00f041ce6a43d17b01b7deeab5dce2fdc00
--- /dev/null
+++ b/day4/lib/car_test.go
@@ -0,0 +1,40 @@
+package lib_test
+
+import (
+	. "go-sinau/day4/lib"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPrepareCar(t *testing.T) {
+	type Cars []*Car
+	type Payload struct {
+		Count int
+		Cars  Cars
+	}
+
+	tt := []struct {
+		Name     string
+		Payload  Payload
+		Expected int
+	}{
+		{
+			Name: "Make 10 cars",
+			Payload: Payload{
+				Count: 10,
+				Cars:  Cars{},
+			},
+			Expected: 10,
+		},
+	}
+
+	for _, test := range tt {
+		t.Run(test.Name, func(t *testing.T) {
+			payload := &test.Payload
+			PrepareCar(payload.Count, (*[]*Car)(&payload.Cars))
+			assert.NotEmpty(t, payload.Cars)
+			assert.Equal(t, len(payload.Cars), test.Expected)
+		})
+	}
+}
diff --git a/day4/lib/race.go b/day4/lib/race.go
new file mode 100644
index 0000000000000000000000000000000000000000..05d9cee87e7829ab7d5f9ce83e96497fd69f5f15
--- /dev/null
+++ b/day4/lib/race.go
@@ -0,0 +1,167 @@
+package lib
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/eaciit/toolkit"
+)
+
+type RaceOption struct {
+	mtx             *sync.Mutex
+	ctx             context.Context
+	cancel          context.CancelFunc
+	wg              *sync.WaitGroup
+	startTime       time.Time
+	winners         chan string
+	distance        int
+	winnersMax      int
+	forceStop       bool
+	winnersFinished []*Car
+	duration        int
+}
+
+func NewRaceOption(wg *sync.WaitGroup) *RaceOption {
+	r := new(RaceOption)
+	r.mtx = new(sync.Mutex)
+	r.ctx, r.cancel = context.WithCancel(context.Background())
+	r.wg = wg
+	r.startTime = time.Now()
+	r.winners = make(chan string)
+
+	return r
+}
+
+func (r *RaceOption) Context() context.Context {
+	return r.ctx
+}
+
+func (r *RaceOption) Cancel() context.CancelFunc {
+	return r.cancel
+}
+
+func (r *RaceOption) StartTime() time.Time {
+	return r.startTime
+}
+
+func (r *RaceOption) SetDistance(distance int) {
+	r.distance = distance
+}
+
+func (r *RaceOption) Distance() int {
+	return r.distance
+}
+
+func (r *RaceOption) AddWinner(winner string) {
+	r.winners <- winner
+}
+
+func (r *RaceOption) Winners() chan string {
+	return r.winners
+}
+
+func (r *RaceOption) Wg() *sync.WaitGroup {
+	return r.wg
+}
+
+func (r *RaceOption) SetWinnersMax(max int) {
+	r.winnersMax = max
+}
+
+func (r *RaceOption) WinnersMax() int {
+	return r.winnersMax
+}
+
+func (r *RaceOption) SetForceStop(force bool) {
+	r.forceStop = force
+}
+
+func (r *RaceOption) ForceStop() bool {
+	return r.forceStop
+}
+
+func (r *RaceOption) SetDuration(duration int) {
+	r.duration = duration
+}
+
+func (r *RaceOption) Duration() int {
+	return r.duration
+}
+
+func (r *RaceOption) WinnersFinished() []*Car {
+	return r.winnersFinished
+}
+
+func (r *RaceOption) IsOver() bool {
+	return len(r.winnersFinished) >= r.winnersMax
+}
+
+func Print(c *Car, distance int, isCar bool) {
+	car := "|=" + c.ID + "->"
+	carTxt := strings.Repeat(" ", c.Lapse) + car
+
+	if len(carTxt) < distance {
+		if isCar {
+			carTxt = "|" + strings.Repeat(" ", distance-len(carTxt)) + carTxt
+		} else {
+			carTxt = "|" + strings.Repeat(" ", distance) + "|"
+		}
+	}
+
+	fmt.Printf("\033[%d;%dH", c.Y, 1)
+	fmt.Println(c.Color(carTxt))
+}
+
+func Race(raceOption *RaceOption, c *Car) {
+	defer func() {
+		raceOption.wg.Done()
+	}()
+
+	distance := raceOption.Distance()
+
+	for i := 0; i <= distance; i++ {
+		select {
+		case <-raceOption.ctx.Done():
+			return
+		default:
+		}
+
+		randInt := toolkit.RandInt(raceOption.duration)
+		times := time.Duration(randInt) * time.Millisecond
+		time.Sleep(times)
+
+		raceOption.mtx.Lock()
+		if !raceOption.IsOver() {
+			Print(c, i, true)
+			c.trip = i
+		}
+		raceOption.mtx.Unlock()
+
+		duration := time.Since(raceOption.startTime)
+		durationPerLap := time.Since(raceOption.startTime)
+
+		if len(c.times) > 0 {
+			lastLap := c.times[i-1]
+			durationPerLap = time.Since(raceOption.startTime.Add(lastLap))
+		}
+
+		c.times = append(c.times, durationPerLap)
+		c.totalTimes = duration
+
+		if i == distance {
+			if raceOption.IsOver() {
+				// stop the race
+				raceOption.cancel()
+			} else {
+				raceOption.mtx.Lock()
+				raceOption.winnersFinished = append(raceOption.winnersFinished, c)
+				raceOption.mtx.Unlock()
+			}
+
+			return
+		}
+	}
+}
diff --git a/day4/lib/race_test.go b/day4/lib/race_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..4a411f39d8dce5f03f636a1b225c383074343edc
--- /dev/null
+++ b/day4/lib/race_test.go
@@ -0,0 +1,105 @@
+package lib_test
+
+import (
+	. "go-sinau/day4/lib"
+	"sync"
+	"testing"
+
+	"github.com/logrusorgru/aurora"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestPrint(t *testing.T) {
+	type Payload struct {
+		Car      Car
+		Distance int
+		IsCar    bool
+	}
+
+	tt := []struct {
+		Name     string
+		Payload  Payload
+		Expected interface{}
+	}{
+		{
+			Name: "Will print winner car",
+			Payload: Payload{
+				Car: Car{
+					ID:    "CAR-1",
+					Y:     1,
+					Lapse: 1,
+					Name:  "John Doe",
+					Color: aurora.Red,
+				},
+				Distance: 100,
+				IsCar:    true,
+			},
+		},
+		{
+			Name: "Will print the pipe",
+			Payload: Payload{
+				Car: Car{
+					ID:    "CAR-1",
+					Y:     1,
+					Lapse: 1,
+					Name:  "John Doe",
+					Color: aurora.Red,
+				},
+				Distance: 100,
+				IsCar:    false,
+			},
+		},
+	}
+
+	for _, test := range tt {
+		t.Run(test.Name, func(t *testing.T) {
+			payload := test.Payload
+			Print(&payload.Car, payload.Distance, payload.IsCar)
+		})
+	}
+}
+
+func TestRace(t *testing.T) {
+	type Payload struct {
+		Car      Car
+		Distance int
+		ChanWon  chan string
+	}
+
+	tt := []struct {
+		Name     string
+		Payload  Payload
+		Expected interface{}
+	}{
+		{
+			Name: "Winner will saved",
+			Payload: Payload{
+				Car: Car{
+					ID:    "CAR-1",
+					Y:     1,
+					Lapse: 1,
+					Name:  "John Doe",
+					Color: aurora.Red,
+				},
+				Distance: 10,
+				ChanWon:  make(chan string),
+			},
+		},
+	}
+
+	wg := new(sync.WaitGroup)
+	raceOpt := NewRaceOption(wg)
+	raceOpt.SetDistance(10)
+	raceOpt.SetWinnersMax(1)
+	raceOpt.SetDuration(10)
+
+	for _, test := range tt {
+		raceOpt.Wg().Add(1)
+		t.Run(test.Name, func(t *testing.T) {
+			payload := test.Payload
+			Race(raceOpt, &payload.Car)
+			assert.GreaterOrEqual(t, len(raceOpt.WinnersFinished()), 1)
+			assert.Equal(t, raceOpt.WinnersFinished()[0].ID, payload.Car.ID)
+		})
+	}
+}
diff --git a/day4/main.go b/day4/main.go
index 3f99d6588f63a1b0518634f5f61e4a0e8e78ca34..886fbc4af55fe6715eb8637112dd05905d501e0a 100644
--- a/day4/main.go
+++ b/day4/main.go
@@ -1,21 +1,27 @@
 package main
 
 import (
-	"context"
 	"flag"
 	"fmt"
+	"sort"
+	"strings"
 	"sync"
 	"time"
 
+	. "go-sinau/day4/lib"
+
 	"github.com/dustin/go-humanize"
 	"github.com/logrusorgru/aurora"
 )
 
 var (
-	carCount = flag.Int("c", 5, "-c=n | n is car count")
-	distance = flag.Int("d", 100, "-d=n | n is track distance")
-	winners  = flag.Int("w", 3, "-w=n | n is winners taken")
+	carCount = flag.Int("c", 20, "-c=n | n is car count")
+	distance = flag.Int("d", 150, "-d=n | n is track distance")
+	winners  = flag.Int("w", 5, "-w=n | n is winners taken")
+	duration = flag.Int("s", 50, "-s=n | n is random duration")
 	cars     []*Car
+
+	winner string
 )
 
 func main() {
@@ -33,35 +39,84 @@ func main() {
 	time.Sleep(3 * time.Second)
 	fmt.Print("\033[2J")
 
-	ctx, cancel := context.WithCancel(context.Background())
-	raceDetails := new(RaceDetails)
-	raceDetails.Details = map[string]*RaceDetail{}
-
 	wg := new(sync.WaitGroup)
 	wg.Add(len(cars))
-
-	mtx := new(sync.Mutex)
+	raceOpt := NewRaceOption(wg)
+	raceOpt.SetDistance(*distance)
+	raceOpt.SetWinnersMax(*winners)
+	raceOpt.SetDuration(*duration)
 
 	for _, c := range cars {
-		Print(c, *distance, false)
-		go Race(ctx, wg, mtx, c, *distance, *winners, raceDetails, cancel)
+		Print(c, raceOpt.Distance(), false)
+		go Race(raceOpt, c)
+	}
+
+	printSeparator := func(sides ...string) {
+		_sides := "-"
+		if len(sides) > 0 {
+			_sides = sides[0]
+		}
+
+		fmt.Printf("\t%s%s%s\n", _sides, strings.Repeat("-", 71), _sides)
+	}
+
+	additionalSpace := len(cars) + 2
+
+	printTitle := func(title string) {
+		fmt.Printf("===== %s =====\n", title)
 	}
 
-	wg.Wait()
+	printSpace := func(additional int) {
+		fmt.Printf("\033[%d;%dH", additionalSpace+additional, 1)
+	}
+
+	getWinners := func() {
+		for i, v := range raceOpt.WinnersFinished() {
+
+			if i == 0 {
+				printSpace(0)
+				printTitle(fmt.Sprintf("WINNERS %v", len(raceOpt.WinnersFinished())))
+			}
 
-	fmt.Printf("\033[%d;%dH", len(cars)+2, 1)
+			printSpace(i + 1)
+			fmt.Println("\t", v.Color(humanize.Ordinal(i+1)), v.Color(v.ID))
 
-	fmt.Println("Winners:")
-	for i := 0; i < *winners; i++ {
-		fmt.Printf("\t%v) %v\n", humanize.Ordinal(i+1), raceDetails.Winners[i])
+			if i >= raceOpt.WinnersMax()-1 {
+				raceOpt.Cancel()
+				break
+			}
+		}
 	}
 
-	fmt.Printf("\n\nDetails\n")
-	fmt.Println("\t| Car \t\t| Speed \t\t| Time \t\t|")
-	for i, v := range raceDetails.DetailsOrdered() {
-		fmt.Printf("\t| %v) %v \t| %vms/1dst \t\t| %v \t|\n", humanize.Ordinal(i+1), v.ID, v.Speed(), v.TotalTime().Format("04:05.000"))
-		if i+1 == *winners {
-			fmt.Println("\t---------------------------------------------------------")
+	raceOpt.Wg().Wait()
+
+	getWinners()
+
+	additionalSpace += (raceOpt.WinnersMax() + 1)
+
+	sort.Slice(cars, func(i, j int) bool {
+		carI, carJ := cars[i], cars[j]
+
+		if carI.Trip() == carJ.Trip() {
+			return carI.TotalTimes().Seconds() < carJ.TotalTimes().Seconds()
+		}
+
+		return carI.Trip() > carJ.Trip()
+	})
+	printSpace(1)
+	printTitle("DETAILS")
+	printSeparator()
+	fmt.Println("\t| Car \t\t\t| Trip \t\t| Time \t\t| Speed \t|")
+	printSeparator("+")
+	for i, c := range cars {
+		if i < 9 {
+			fmt.Printf("\t| %v) %v \t\t| %v du \t| %5f s \t| %5f du/s |\n", c.Color(humanize.Ordinal(i+1)), c.Color(c.ID), c.Color(c.Trip()), c.Color(c.TotalTimes().Seconds()), c.Color(c.Speed()))
+		} else {
+			fmt.Printf("\t| %v) %v \t| %v du \t| %5f s \t| %5f du/s |\n", c.Color(humanize.Ordinal(i+1)), c.Color(c.ID), c.Color(c.Trip()), c.Color(c.TotalTimes().Seconds()), c.Color(c.Speed()))
+		}
+		if i == raceOpt.WinnersMax()-1 {
+			printSeparator("+")
 		}
 	}
+	printSeparator()
 }
diff --git a/day4/race.go b/day4/race.go
deleted file mode 100644
index f3b59db8027b5aa4ff6383c5076c13f2cd2d9d13..0000000000000000000000000000000000000000
--- a/day4/race.go
+++ /dev/null
@@ -1,80 +0,0 @@
-package main
-
-import (
-	"time"
-
-	"github.com/novalagung/gubrak/v2"
-)
-
-type RaceDetail struct {
-	ID    string
-	order int
-	Times []int
-}
-
-type RaceDetails struct {
-	Winners []string
-	Details map[string]*RaceDetail
-}
-
-func (r *RaceDetails) DetailsOrdered() []*RaceDetail {
-	res := []*RaceDetail{}
-
-	detailList := []*RaceDetail{}
-
-	for _, v := range r.Details {
-		detailList = append(detailList, v)
-	}
-
-	filterWinner := gubrak.From(detailList).
-		Filter(func(each *RaceDetail) bool {
-			return each.Order() > 0
-		}).OrderBy(func(each *RaceDetail) int {
-		return each.Order()
-	}).
-		Result()
-
-	if filterWinner != nil {
-		res = append(res, filterWinner.([]*RaceDetail)...)
-	}
-
-	filterLooser := gubrak.From(detailList).
-		Filter(func(each *RaceDetail) bool {
-			return each.Order() < 1
-		}).OrderBy(func(each *RaceDetail) int {
-		return each.Speed()
-	}).
-		Result()
-
-	if filterLooser != nil {
-		res = append(res, filterLooser.([]*RaceDetail)...)
-	}
-
-	return res
-}
-
-func (r *RaceDetail) TotalTimeMs() int {
-	sum := 0
-
-	for _, v := range r.Times {
-		sum += v
-	}
-
-	return sum
-}
-
-func (r *RaceDetail) TotalTime() time.Time {
-	return time.Unix(0, int64(r.TotalTimeMs())*int64(time.Millisecond))
-}
-
-func (r *RaceDetail) Speed() int {
-	return r.TotalTimeMs() / len(r.Times)
-}
-
-func (r *RaceDetail) Order() int {
-	return r.order
-}
-
-func (r *RaceDetail) SetOrder(order int) {
-	r.order = order
-}
diff --git a/go.mod b/go.mod
index 13562179c46e46f7c4a2478c8d3a862745f037b6..d7aadeb83b6822336ea4797e8d2922c172145011 100644
--- a/go.mod
+++ b/go.mod
@@ -5,12 +5,8 @@ go 1.17
 require github.com/stretchr/testify v1.7.1
 
 require (
-	github.com/davecgh/go-spew v1.1.0 // indirect
-	github.com/dustin/go-humanize v1.0.0 // indirect
-	github.com/eaciit/toolkit v0.0.0-20210610161449-593d5fadf78e // indirect
-	github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
-	github.com/novalagung/gubrak/v2 v2.0.1 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
-	golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
-	gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+	github.com/dustin/go-humanize v1.0.0
+	github.com/eaciit/toolkit v0.0.0-20210610161449-593d5fadf78e
+	github.com/logrusorgru/aurora v2.0.3+incompatible
+	github.com/novalagung/gubrak/v2 v2.0.1
 )
diff --git a/go.sum b/go.sum
index 0fe50678e03f9dc995d39de6cb1b0463a07c3100..beb03d152f141d46d2871f5ac65301454f18f4d6 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,4 @@
+github.com/DefinitelyMod/gocsv v0.0.0-20181205141819-acfa5f112b45 h1:+OD9vawobD89HK04zwMokunBCSEeAb08VWAHPUMg+UE=
 github.com/DefinitelyMod/gocsv v0.0.0-20181205141819-acfa5f112b45/go.mod h1:+nlrAh0au59iC1KN5RA1h1NdiOQYlNOBrbtE1Plqht4=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -20,6 +21,7 @@ golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCT
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
diff --git a/makefile b/makefile
index ac7394697ff008af2fb73509baedb88d626d96f6..5b82bce8c372690637a372efd47151872883ebe2 100644
--- a/makefile
+++ b/makefile
@@ -1,23 +1,23 @@
 day1.run:
-	go run ./day1/vars.go ./day1/main.go
+	go run ./day1/main.go
 
 day1.test:
 	go test ./day1/... -v -cover
 
 day2.run:
-	go run ./day2/word.go ./day2/main.go
+	go run ./day2/main.go
 
 day2.test:
 	go test ./day2/... -v -cover
 
 day3.run:
-	go run ./day3/car.go ./day3/main.go
+	go run ./day3/main.go
 
 day3.test:
 	go test ./day3/... -v -cover
 
 day4.run:
-	go run ./day4/race.go ./day4/car.go ./day4/main.go
+	go run ./day4/main.go
 
 day4.test:
 	go test ./day4/... -v -cover