<commit_before>// Copyright (c) 2014-2015 by <NAME>. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`encoding/binary`
	`os`
	`sort`
)

// Many pages make a thick book.
type Book struct {
	fileName string
	entries  int64
}

// Opening book record: the fields are exported for binary.Read().
type Entry struct {
	Key   uint64
	Move  uint16
	Score uint16
	Learn uint32
}

func NewBook(bookFile string) (*Book, error) {
	book := &Book{fileName: bookFile}

	if fi, err := os.Stat(book.fileName); err != nil {
		return nil, err
	} else {
		book.entries = fi.Size() / 16
	}

	return book, nil
}

func (b *Book) pickMove(position *Position) Move {
	entries := b.lookup(position)
	switch length := len(entries); length {
	case 0:
		// TODO: set the "useless book" flag after a few misses.
		return 0
	case 1:
		// The only move available.
		return b.move(position, entries[0])
	default:
		// Sort book entries by score and pick among two best moves.
		sort.Sort(byBookScore{entries})
		best := min(2, len(entries))
		return b.move(position, entries[Random(best)])
	}
}

func (b *Book) lookup(position *Position) (entries []Entry) {
	var entry Entry

	file, err := os.Open(b.fileName)
	if err != nil {
		return
	}
	defer file.Close()

	key, _ := position.polyglot()

	// Since book entries are ordered by polyglot key we can use binary
	// search to find *first* book entry that matches the position.
	first, current, last := int64(-1), int64(0), b.entries
	for first < last {
		current = (first + last) / 2
		file.Seek(current*16, 0)
		binary.Read(file, binary.BigEndian, &entry)
		if key <= entry.Key {
			last = current
		} else {
			first = current + 1
		}
	}
<commit_msg>Use existing position hash when loading book move<commit_after>// Copyright (c) 2014-2015 by <NAME>. All Rights Reserved.
// Use of this source code is governed by a MIT-style license that can
// be found in the LICENSE file.

package donna

import (
	`encoding/binary`
	`os`
	`sort`
)

// Many pages make a thick book.
type Book struct {
	fileName string
	entries  int64
}

// Opening book record: the fields are exported for binary.Read().
type Entry struct {
	Key   uint64
	Move  uint16
	Score uint16
	Learn uint32
}

func NewBook(bookFile string) (*Book, error) {
	book := &Book{fileName: bookFile}

	if fi, err := os.Stat(book.fileName); err != nil {
		return nil, err
	} else {
		book.entries = fi.Size() / 16
	}

	return book, nil
}

func (b *Book) pickMove(position *Position) Move {
	entries := b.lookup(position)
	switch length := len(entries); length {
	case 0:
		// TODO: set the "useless book" flag after a few misses.
		return 0
	case 1:
		// The only move available.
		return b.move(position, entries[0])
	default:
		// Sort book entries by score and pick among two best moves.
		sort.Sort(byBookScore{entries})
		best := min(2, len(entries))
		return b.move(position, entries[Random(best)])
	}
}

func (b *Book) lookup(position *Position) (entries []Entry) {
	var entry Entry

	file, err := os.Open(b.fileName)
	if err != nil {
		return
	}
	defer file.Close()

	key := position.hash

	// Since book entries are ordered by polyglot key we can use binary
	// search to find *first* book entry that matches the position.
	first, current, last := int64(-1), int64(0), b.entries
	for first < last {
		current = (first + last) / 2
		file.Seek(current*16, 0)
		binary.Read(file, binary.BigEndian, &entry)
		if key <= entry.Key {
			last = current
		} else {
			first = current + 1
		}
	}

{'fraction_non_alphanumeric': 0.12139052425007009, 'fraction_numerical': 0.020185029436501262, 'mean_word_length': 3.3143893591293834, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5635044', 'n_tokens_mistral': 1295, 'n_tokens_neox': 1194, 'n_words': 437}