-
Notifications
You must be signed in to change notification settings - Fork 17
Kadai3 akuchii #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Kadai3 akuchii #41
Changes from 2 commits
7d4820d
b3217ec
c8f07b2
94a1abd
26d3564
c33add5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package game | ||
|
||
import ( | ||
"bufio" | ||
"context" | ||
"fmt" | ||
"io" | ||
"math/rand" | ||
"os" | ||
"time" | ||
) | ||
|
||
// Game generates typing game | ||
type Game struct { | ||
writer io.Writer | ||
idx int | ||
words []string | ||
timeout int | ||
} | ||
|
||
func init() { | ||
rand.Seed(time.Now().UnixNano()) | ||
} | ||
|
||
// NewGame generates new Game instance | ||
func NewGame(w io.Writer, words []string, timeout int) *Game { | ||
return &Game{writer: w, words: words, timeout: timeout} | ||
} | ||
|
||
// Start starts typing game | ||
func (g Game) Start() int { | ||
ctx := context.Background() | ||
ctx, cancel := context.WithTimeout(ctx, time.Duration(g.timeout)*time.Second) | ||
defer cancel() | ||
|
||
ch := input(os.Stdin) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. せっかく There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、たしかに |
||
cnt := 0 | ||
LOOP: | ||
for { | ||
g.setNewIdx() | ||
fmt.Fprintln(g.writer, "> "+g.word()) | ||
select { | ||
case v := <-ch: | ||
if v == g.word() { | ||
cnt++ | ||
fmt.Fprintln(g.writer, "correct!") | ||
} else { | ||
fmt.Fprintln(g.writer, "incorrect!") | ||
} | ||
case <-ctx.Done(): | ||
break LOOP | ||
} | ||
} | ||
return cnt | ||
} | ||
|
||
func (g *Game) setNewIdx() { | ||
rand.Seed(time.Now().UnixNano()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. initでSeedをセットしているので、Seedは毎回呼ばなくても問題ありません There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. たしかにそうですね! |
||
g.idx = rand.Intn(len(g.words)) | ||
} | ||
|
||
func (g Game) word() string { | ||
return g.words[g.idx] | ||
} | ||
|
||
func input(r io.Reader) <-chan string { | ||
ch := make(chan string) | ||
go func() { | ||
s := bufio.NewScanner(r) | ||
for s.Scan() { | ||
ch <- s.Text() | ||
} | ||
close(ch) | ||
}() | ||
return ch | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/gopherdojo/dojo4/kadai3-1/akuchii/game" | ||
) | ||
|
||
func main() { | ||
timeout := 5 | ||
game := game.NewGame(os.Stdout, getWords(), timeout) | ||
cnt := game.Start() | ||
fmt.Printf("\nfinish!\ncorrect count is %d\n", cnt) | ||
os.Exit(0) | ||
} | ||
|
||
func getWords() []string { | ||
return []string{"csharp", "python", "perl", "cplusplus", "ruby", "golang", "scala"} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package downloader | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"golang.org/x/sync/errgroup" | ||
) | ||
|
||
// Downloader downloads data in parallel | ||
type Downloader struct { | ||
url string | ||
fileSize uint | ||
splitNum uint | ||
fileName string | ||
} | ||
|
||
// Range defines range of data of start and end | ||
type Range struct { | ||
start uint | ||
end uint | ||
idx uint | ||
} | ||
|
||
// NewDownloader generate new Downloader instance | ||
func NewDownloader(url string, splitNum uint) *Downloader { | ||
return &Downloader{ | ||
url: url, | ||
splitNum: splitNum, | ||
} | ||
} | ||
|
||
// Prepare prepares for download in parallel | ||
func (d *Downloader) Prepare() error { | ||
res, err := http.Head(d.url) | ||
if err != nil { | ||
return err | ||
} | ||
d.fileSize = uint(res.ContentLength) | ||
|
||
elems := strings.Split(d.url, "/") | ||
d.fileName = elems[len(elems)-1] | ||
|
||
return nil | ||
} | ||
|
||
// Download downloads in parallel | ||
func (d Downloader) Download() error { | ||
var eg errgroup.Group | ||
|
||
for i := uint(0); i < d.splitNum; i++ { | ||
r := d.makeRange(i) | ||
eg.Go(func() error { | ||
return d.request(r) | ||
}) | ||
} | ||
if err := eg.Wait(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// GetFileName gets name of download file | ||
func (d Downloader) GetFileName() string { | ||
return d.fileName | ||
} | ||
|
||
// MergeFiles merges files downloads in parallel | ||
func (d Downloader) MergeFiles() error { | ||
f, err := os.Create(d.fileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer f.Close() | ||
for i := uint(0); i < d.splitNum; i++ { | ||
tmpFileName := fmt.Sprintf("%s.%d", d.fileName, i) | ||
tmpFile, err := os.Open(tmpFileName) | ||
if err != nil { | ||
return err | ||
} | ||
io.Copy(f, tmpFile) | ||
tmpFile.Close() | ||
if err := os.Remove(tmpFileName); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (d Downloader) request(r Range) error { | ||
req, _ := http.NewRequest("GET", d.url, nil) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://golang.org/pkg/net/http/#pkg-constants
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. エラーの可能性もあるので、エラーは捨てない方が良いと思います There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 定数あるの知りませんでした。ありがとうございます。 |
||
req.Header.Set("Range", fmt.Sprintf("bytes=%d-%d", r.start, r.end)) | ||
client := new(http.Client) | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
tmpFileName := fmt.Sprintf("%s.%d", d.fileName, r.idx) | ||
file, err := os.Create(tmpFileName) | ||
if err != nil { | ||
return err | ||
} | ||
defer file.Close() | ||
|
||
if _, err := io.Copy(file, resp.Body); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (d Downloader) makeRange(i uint) Range { | ||
splitSize := d.fileSize / d.splitNum | ||
start := i * splitSize | ||
end := (i+1)*splitSize - 1 | ||
|
||
if i == d.splitNum { | ||
end = d.fileSize | ||
} | ||
|
||
return Range{ | ||
start: start, | ||
end: end, | ||
idx: i, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
|
||
"github.com/gopherdojo/dojo4/kadai3-2/akuchii/downloader" | ||
) | ||
|
||
func main() { | ||
if len(os.Args) != 2 { | ||
fmt.Println("require url") | ||
os.Exit(1) | ||
} | ||
|
||
var splitNum uint = 4 | ||
url := os.Args[1] | ||
d := downloader.NewDownloader(url, splitNum) | ||
|
||
if err := d.Prepare(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
if err := d.Download(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
|
||
if err := d.MergeFiles(); err != nil { | ||
fmt.Println(err) | ||
os.Exit(1) | ||
} | ||
fmt.Printf("download complete! : %v\n", d.GetFileName()) | ||
os.Exit(0) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timeout
、利用する側から単位がわからないと何渡せばよいかわからなくなってしまう(秒、分、ミリ秒など)ので、型をtime.Duration
にするとわかりやすくなったりしますThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
利用する側のことを考えて型を考えるのがよいんですね
26d3564