diff --git a/kadai3-1/happylifetaka/typing.go b/kadai3-1/happylifetaka/typing.go new file mode 100644 index 0000000..40516d7 --- /dev/null +++ b/kadai3-1/happylifetaka/typing.go @@ -0,0 +1,12 @@ +package main + +import ( + "os" + + "github.com/happylifetaka/dojo4/kadai3-1/happylifetaka/typinggame" +) + +func main() { + var t typinggame.TypingGame + t.Start(os.Stdin) +} diff --git a/kadai3-1/happylifetaka/typinggame/typinggame.go b/kadai3-1/happylifetaka/typinggame/typinggame.go new file mode 100644 index 0000000..4845157 --- /dev/null +++ b/kadai3-1/happylifetaka/typinggame/typinggame.go @@ -0,0 +1,82 @@ +package typinggame + +import ( + "bufio" + "fmt" + "io" + "math/rand" + "time" +) + +//TypingGame 1 minute typing game. +type TypingGame int + +//Start start 1 minute typing game. +//Param +//r:io.Reader example:os.Stdin +func (t *TypingGame) Start(r io.Reader) { + ch1 := input(r) + ch2 := wait(60) + words := []string{"apple", "banana", "cherry", "plum", "grape", "pineapple"} + + shuffle(words) + i := 0 + //success count + sucessCnt := 0 + //fail count + failCnt := 0 + fmt.Println("try typing.1 minute.") + fmt.Println(words[i]) + +TIMEOUT_LABEL: + for { + select { + case msg := <-ch1: + if words[i] == msg { + if len(words) <= (i + 1) { + i = 0 + } else { + i++ + } + sucessCnt++ + } else { + fmt.Println("miss.retyping words.") + failCnt++ + } + fmt.Println(words[i]) + case <-ch2: + fmt.Println("") + fmt.Println("time up.success count:", sucessCnt, " fail count:", failCnt) + + break TIMEOUT_LABEL + } + } +} + +func shuffle(a []string) { + for i := len(a) - 1; i >= 0; i-- { + j := rand.Intn(i + 1) + a[i], a[j] = a[j], a[i] + } +} + +func input(r io.Reader) <-chan string { + ch := make(chan string) + go func() { + s := bufio.NewScanner(r) + defer close(ch) + for s.Scan() { + ch <- s.Text() + } + }() + return ch +} + +func wait(sec int) <-chan bool { + ch := make(chan bool) + go func() { + time.Sleep(time.Duration(sec) * time.Second) + ch <- true + }() + return ch +} diff --git a/kadai3-2/happylifetaka/downloader.go b/kadai3-2/happylifetaka/downloader.go new file mode 100644 index 0000000..5ca4a7d --- /dev/null +++ b/kadai3-2/happylifetaka/downloader.go @@ -0,0 +1,41 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/happylifetaka/dojo4/kadai3-2/happylifetaka/downloader" +) + +type downloadByteInfo struct { + start int64 + end int64 +} + +func main() { + div := flag.Int64("div", 3, "file download division") + usageMsg := "udage:downloader [-div] url saveFilePath" + flag.Usage = func() { + fmt.Println(usageMsg) + flag.PrintDefaults() + os.Exit(0) + } + flag.Parse() + args := flag.Args() + if len(args) != 2 { + fmt.Println("parameter error.") + fmt.Println(usageMsg) + flag.PrintDefaults() + os.Exit(0) + } + url := args[0] + saveFilePath := args[1] + + var d downloader.Downloader + if err := d.Download(url, saveFilePath, *div); err != nil { + fmt.Println(err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/kadai3-2/happylifetaka/downloader/downloader.go b/kadai3-2/happylifetaka/downloader/downloader.go new file mode 100644 index 0000000..3b8566d --- /dev/null +++ b/kadai3-2/happylifetaka/downloader/downloader.go @@ -0,0 +1,175 @@ +package downloader + +import ( + "errors" + "fmt" + "io" + "net/http" + "os" + "strconv" + + "golang.org/x/sync/errgroup" +) + +// Downloader divide support donwloader +type Downloader int + +type downloadInfo struct { + startByte int64 + endByte int64 +} + +type deleteFileError struct { + filename string + err error +} + +// Download download action +// description:Download specified URL. +// parameter +// url :download target url. +// saveFilePath:save file path. +// div:Number of divided downloads. +func (d *Downloader) Download(url, saveFilePath string, div int64) error { + res, err := http.Head(url) + if err != nil { + fmt.Println("http Head error") + return err + } + if res.StatusCode != 200 { + fmt.Println("bad status code") + fmt.Println("status code:", res.StatusCode) + return err + } + + if !canRangeDownload(res.Header) { + fmt.Println("[warn]range download not support.") + div = 1 + } + + di := splitDownloadLength(res.ContentLength, div) + filenames := make([]string, div) + + var eg errgroup.Group + + i := 1 + for _, d := range di { + j := i + eg.Go(func() error { + err := rangeDownload(j, url, d.startByte, d.endByte) + return err + }) + filenames[i-1] = strconv.Itoa(j) + ".temp.download" + i++ + } + if err := eg.Wait(); err != nil { + fmt.Println("download error") + return err + } + + if err := joinFiles(filenames, saveFilePath); err != nil { + fmt.Println("join file error") + return err + } + + deleteFileError := deleteFiles(filenames) + + if len(deleteFileError) != 0 { + for _, e := range deleteFileError { + fmt.Printf("[delete file error.%s %s", e.filename, e.err) + } + return errors.New("download fail") + } + + fmt.Println("download finish.") + return nil +} + +func canRangeDownload(h http.Header) bool { + accept := h.Get("Accept-Ranges") + + if accept == "bytes" { + return true + } else { + return false + } +} + +func splitDownloadLength(length, div int64) []downloadInfo { + + divLength := length / div + + a := make([]downloadInfo, div) + + var i int64 + for i = 0; i < div; i++ { + s := i * divLength + e := (i+1)*divLength - 1 + + a[i] = downloadInfo{s, e} + } + return a +} + +func rangeDownload(index int, url string, s, e int64) error { + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return err + } + + req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", s, e)) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + outPath := strconv.Itoa(index) + ".temp.download" + out, err := os.Create(outPath) + if err != nil { + return err + } + defer out.Close() + + _, copyerr := io.Copy(out, res.Body) + if copyerr != nil { + return err + } + return nil +} + +func joinFiles(filenames []string, saveFilePath string) error { + files := make([]io.Reader, len(filenames)) + + for i, filename := range filenames { + files[i], _ = os.Open(filename) + } + + src := io.MultiReader(files...) + + dst, err := os.Create(saveFilePath) + if err != nil { + return err + } + defer dst.Close() + + _, err = io.Copy(dst, src) + if err != nil { + return err + } + + return nil +} + +func deleteFiles(filenames []string) []deleteFileError { + result := []deleteFileError{} + + for _, f := range filenames { + err := os.Remove(f) + if err != nil { + result = append(result, deleteFileError{f, err}) + } + } + return result +}