github.com/joshprzybyszewski/masyu@v0.0.0-20230508015604-f31a025f6e7e/results/generate.go (about)

     1  package results
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"runtime"
     7  	"sort"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/joshprzybyszewski/masyu/fetch"
    12  	"github.com/joshprzybyszewski/masyu/model"
    13  	"github.com/joshprzybyszewski/masyu/solve"
    14  
    15  	"github.com/shirou/gopsutil/cpu"
    16  )
    17  
    18  const (
    19  	numPuzzlesPerIter = 500
    20  
    21  	readmeFilename     = `README.md`
    22  	resultsStartMarker = `<resultsMarker>`
    23  	resultsStopMarker  = `</resultsMarker>`
    24  )
    25  
    26  const (
    27  	resultsTimeout = 90 * time.Second
    28  )
    29  
    30  func Update() {
    31  	var allResults [model.MaxIterator + 1][]time.Duration
    32  	for iter := model.MinIterator; iter <= model.MaxIterator; iter++ {
    33  		durs, err := getResults(iter, resultsTimeout)
    34  		if err != nil {
    35  			fmt.Printf("Error: %+v\n", err)
    36  			continue
    37  		}
    38  		allResults[iter] = append(allResults[iter], durs...)
    39  	}
    40  
    41  	var sb strings.Builder
    42  
    43  	sb.WriteByte('\n')
    44  	sb.WriteByte('\n')
    45  	sb.WriteString(fmt.Sprintf("_GOOS: %s_\n\n", runtime.GOOS))
    46  	sb.WriteString(fmt.Sprintf("_GOARCH: %s_\n\n", runtime.GOARCH))
    47  
    48  	if stats, err := cpu.Info(); err == nil {
    49  		sb.WriteString(fmt.Sprintf("_cpu: %s_\n\n", stats[0].ModelName))
    50  	}
    51  
    52  	sb.WriteString(fmt.Sprintf("_Solve timeout: %s_\n\n", resultsTimeout))
    53  
    54  	sb.WriteString("|Puzzle|Min|p25|Median|p75|p95|max|sample size|\n")
    55  	sb.WriteString("|-|-|-|-|-|-|-|-:|\n")
    56  
    57  	for iter := model.MinIterator; iter < model.Iterator(len(allResults)); iter++ {
    58  		sb.WriteString(getTableRow(iter, allResults[iter]))
    59  	}
    60  
    61  	sb.WriteString(fmt.Sprintf("\n_Last Updated: %s_\n", time.Now().Format(time.RFC822)))
    62  
    63  	writeResultsToReadme(sb.String())
    64  
    65  	fmt.Printf("Results updated.\n")
    66  }
    67  
    68  func getResults(
    69  	iter model.Iterator,
    70  	timeout time.Duration,
    71  ) ([]time.Duration, error) {
    72  	fmt.Printf("Starting %s\n", iter)
    73  
    74  	inputs, err := fetch.ReadN(iter, numPuzzlesPerIter)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	durs := make([]time.Duration, 0, len(inputs))
    80  
    81  	for i, sr := range inputs {
    82  		ns := sr.Input.ToNodes()
    83  
    84  		t0 := time.Now()
    85  		sol, err := solve.FromNodesWithTimeout(
    86  			iter.GetSize(),
    87  			ns,
    88  			timeout,
    89  		)
    90  		dur := time.Since(t0)
    91  		durs = append(durs, dur)
    92  		if i%10 == 0 {
    93  			fmt.Printf("\n%2d: ", i/10)
    94  		}
    95  		if err != nil {
    96  			fmt.Print("X")
    97  			continue
    98  		}
    99  		if dur > 5*time.Second {
   100  			fmt.Print("!")
   101  		} else if dur > time.Second {
   102  			fmt.Print("*")
   103  		} else if dur > 500*time.Millisecond {
   104  			fmt.Print("-")
   105  		} else {
   106  			fmt.Print(".")
   107  		}
   108  
   109  		if sr.Answer != `` {
   110  			if sr.Answer != sol.ToAnswer() {
   111  				fmt.Printf("\nIs this correct?\n")
   112  				fmt.Printf("%s\n", sr.Input)
   113  				fmt.Printf("%s\n", sol.Pretty(ns))
   114  			}
   115  		}
   116  
   117  		for numGCs := 0; numGCs < 3; numGCs++ {
   118  			time.Sleep(10 * time.Millisecond)
   119  			runtime.GC()
   120  		}
   121  	}
   122  
   123  	fmt.Printf("\nFinished %s\n\n", iter)
   124  	fmt.Printf("%s\n\n", getTableRow(iter, durs))
   125  	return durs, nil
   126  }
   127  
   128  func getTableRow(
   129  	iter model.Iterator,
   130  	durs []time.Duration,
   131  ) string {
   132  	if len(durs) == 0 {
   133  		return ``
   134  	}
   135  
   136  	sort.Slice(durs, func(i, j int) bool {
   137  		return durs[i] < durs[j]
   138  	})
   139  
   140  	var sb strings.Builder
   141  	sb.WriteString(fmt.Sprintf("|%s|", iter))
   142  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[0])))
   143  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)/4])))
   144  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)/2])))
   145  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)*3/4])))
   146  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)*19/20])))
   147  	sb.WriteString(fmt.Sprintf("%s|", roundDuration(durs[len(durs)-1])))
   148  	sb.WriteString(fmt.Sprintf("%d|\n", len(durs)))
   149  	return sb.String()
   150  }
   151  
   152  func roundDuration(
   153  	d time.Duration,
   154  ) time.Duration {
   155  
   156  	if d > time.Second {
   157  		return d.Truncate(10 * time.Millisecond)
   158  	}
   159  
   160  	if d > time.Millisecond {
   161  		return d.Truncate(10 * time.Microsecond)
   162  	}
   163  
   164  	if d > time.Microsecond {
   165  		return d.Truncate(10 * time.Nanosecond)
   166  	}
   167  
   168  	return d
   169  }
   170  
   171  func writeResultsToReadme(
   172  	res string,
   173  ) {
   174  
   175  	fmt.Printf("Writing these results to the readme:\n%s\n", res)
   176  
   177  	content, err := getREADMEContent()
   178  	if err != nil {
   179  		fmt.Printf("Error fetching readme content: %s\n", err.Error())
   180  		return
   181  	}
   182  
   183  	i1 := strings.Index(content, resultsStartMarker)
   184  	if i1 < 0 {
   185  		fmt.Printf("Could not find %q in the README\n", resultsStartMarker)
   186  		return
   187  	}
   188  	i2 := strings.Index(content, resultsStopMarker)
   189  	if i2 < 0 {
   190  		fmt.Printf("Could not find %q in the README\n", resultsStopMarker)
   191  		return
   192  	}
   193  
   194  	newContent := content[:i1+len(resultsStartMarker)] + res + content[i2:]
   195  
   196  	err = writeREADME(newContent)
   197  	if err != nil {
   198  		fmt.Printf("Error writing readme content: %s\n", err.Error())
   199  		return
   200  	}
   201  }
   202  
   203  func getREADMEContent() (string, error) {
   204  	data, err := os.ReadFile(readmeFilename)
   205  	if err != nil {
   206  		return ``, err
   207  	}
   208  	return string(data), nil
   209  }
   210  
   211  func writeREADME(
   212  	content string,
   213  ) error {
   214  	f, err := os.Create(readmeFilename)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	defer f.Close()
   219  
   220  	_, err = f.WriteString(content)
   221  	if err != nil {
   222  		return err
   223  	}
   224  	return nil
   225  }