<commit_before>package sudoku

import (
	"fmt"
	"math/rand"
)

type blockBlockInteractionTechnique struct {
	*basicSolveTechnique
}

func (self *blockBlockInteractionTechnique) HumanLikelihood() float64 {
	//TODO: think more about how difficult this technique is.
	return self.difficultyHelper(60.0)
}

func (self *blockBlockInteractionTechnique) Find(grid *Grid) []*SolveStep {

	var results []*SolveStep

	pairs := pairwiseBlocks(grid)

	//We're going to be looking at a lot of blocks again and again, so might as well cache this.
	unfilledCellsForBlock := make([]CellSlice, DIM)
	filledNumsForBlock := make([]IntSlice, DIM)
	for i := 0; i < DIM; i++ {
		unfilledCellsForBlock[i] = grid.Block(i).FilterByHasPossibilities()
		filledNumsForBlock[i] = grid.Block(i).FilledNums()
	}

	//For each pair of blocks (in random order)
	for _, pairIndex := range rand.Perm(len(pairs)) {
		pair := pairs[pairIndex]
		excludeNums := filledNumsForBlock[pair[0]].toIntSet().union(filledNumsForBlock[pair[1]].toIntSet())
		for _, i := range rand.Perm(DIM) {
			//Skip numbers entirely where either of the blocks has a cell with it set, since there obviously
			//won't be any cells in both blocks that have that possibility.
			if _, ok := excludeNums[i]; ok {
				continue
			}
			//Find cells in each block that have that possibility.
			firstBlockCells := unfilledCellsForBlock[pair[0]].FilterByPossible(i)
			secondBlockCells := unfilledCellsForBlock[pair[1]].FilterByPossible(i)

			//Now we need to figure out if these blocks are in the same row or same col
			var majorAxisIsRow bool
			rowOne, colOne, _, _ := grid.blockExtents(pair[0])
			rowTwo, colTwo, _, _ := grid.blockExtents(pair[1])

			if rowOne == rowTwo {
				majorAxisIsRow = true
			} else if colOne == colTwo {
				majorAxisIsRow = false
			} else {
				panic("This shouldn't happen")
			}

			var blockOneIndexes IntSlice
			var blockTwoIndexes IntSlice

			if majorAxisIsRow {
				blockOneIndexes = firstBlockCells.CollectNums(getRow).Unique()
				blockTwoIndexes = secondBlockCells.CollectNums(getRow).Unique()
			} else {
				blockOneIndexes = firstBlockCells.CollectNums(getCol).Unique()
				blockTwoIndexes = secondBlockCells.CollectNums(getCol).Unique()
			}

			if len(blockOneIndexes) != 2 || len(blockTwoIndexes) != 2 {
				//There can only be two rows or columns in play for this technique to work.
				continue
			}

			if !blockOneIndexes.SameContentAs(blockTwoIndexes) {
				//Meh, they didn't line up into the same major axis cells.
				continue
			}

			var targetCells CellSlice

			if majorAxisIsRow {
				targetCells = grid.Row(blockOneIndexes[0])
				targetCells = append(targetCells, grid.Row(blockOneIndexes[1])...)
				targetCells = targetCells.RemoveCells(grid.Block(pair[0])).RemoveCells(grid.Block(pair[1]))
			} else {
				targetCells = grid.Col(blockOneIndexes[0])
				targetCells = append(targetCells, grid.Col(blockOneIndexes[1])...)
				targetCells = targetCells.RemoveCells(grid.Block(pair[0])).RemoveCells(grid.Block(pair[1]))
			}

			//Okay, we have a possible set. Now we need to create a step.
			step := &SolveStep{
				self,
				targetCells,
				[]int{i},
				append(firstBlockCells, secondBlockCells...),
				nil,
			}

			if step.IsUseful(grid) {
				results = append(results, step)
			}
		}

	}
	return results
}

func (self *blockBlockInteractionTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) != 1 {
		return ""<commit_msg>Block block implements new Find signature<commit_after>package sudoku

import (
	"fmt"
	"math/rand"
)

type blockBlockInteractionTechnique struct {
	*basicSolveTechnique
}

func (self *blockBlockInteractionTechnique) HumanLikelihood() float64 {
	//TODO: think more about how difficult this technique is.
	return self.difficultyHelper(60.0)
}

func (self *blockBlockInteractionTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {

	pairs := pairwiseBlocks(grid)

	//We're going to be looking at a lot of blocks again and again, so might as well cache this.
	unfilledCellsForBlock := make([]CellSlice, DIM)
	filledNumsForBlock := make([]IntSlice, DIM)
	for i := 0; i < DIM; i++ {
		unfilledCellsForBlock[i] = grid.Block(i).FilterByHasPossibilities()
		filledNumsForBlock[i] = grid.Block(i).FilledNums()
	}

	//For each pair of blocks (in random order)
	for _, pairIndex := range rand.Perm(len(pairs)) {

		pair := pairs[pairIndex]
		excludeNums := filledNumsForBlock[pair[0]].toIntSet().union(filledNumsForBlock[pair[1]].toIntSet())
		for _, i := range rand.Perm(DIM) {

			//See if we should stop doing work
			select {
			case <-done:
				return
			default:
			}

			//Skip numbers entirely where either of the blocks has a cell with it set, since there obviously
			//won't be any cells in both blocks that have that possibility.
			if _, ok := excludeNums[i]; ok {
				continue
			}
			//Find cells in each block that have that possibility.
			firstBlockCells := unfilledCellsForBlock[pair[0]].FilterByPossible(i)
			secondBlockCells := unfilledCellsForBlock[pair[1]].FilterByPossible(i)

			//Now we need to figure out if these blocks are in the same row or same col
			var majorAxisIsRow bool
			rowOne, colOne, _, _ := grid.blockExtents(pair[0])
			rowTwo, colTwo, _, _ := grid.blockExtents(pair[1])

			if rowOne == rowTwo {
				majorAxisIsRow = true
			} else if colOne == colTwo {
				majorAxisIsRow = false
			} else {
				panic("This shouldn't happen")
			}

			var blockOneIndexes IntSlice
			var blockTwoIndexes IntSlice

			if majorAxisIsRow {
				blockOneIndexes = firstBlockCells.CollectNums(getRow).Unique()
				blockTwoIndexes = secondBlockCells.CollectNums(getRow).Unique()
			} else {
				blockOneIndexes = firstBlockCells.CollectNums(getCol).Unique()
				blockTwoIndexes = secondBlockCells.CollectNums(getCol).Unique()
			}

			if len(blockOneIndexes) != 2 || len(blockTwoIndexes) != 2 {
				//There can only be two rows or columns in play for this technique to work.
				continue
			}

			if !blockOneIndexes.SameContentAs(blockTwoIndexes) {
				//Meh, they didn't line up into the same major axis cells.
				continue
			}

			var targetCells CellSlice

			if majorAxisIsRow {
				targetCells = grid.Row(blockOneIndexes[0])
				targetCells = append(targetCells, grid.Row(blockOneIndexes[1])...)
				targetCells = targetCells.RemoveCells(grid.Block(pair[0])).RemoveCells(grid.Block(pair[1]))
			} else {
				targetCells = grid.Col(blockOneIndexes[0])
				targetCells = append(targetCells, grid.Col(blockOneIndexes[1])...)
				targetCells = targetCells.RemoveCells(grid.Block(pair[0])).RemoveCells(grid.Block(pair[1]))
			}

			//Okay, we have a possible set. Now we need to create a step.
			step := &SolveStep{
				self,
				targetCells,
				[]int{i},
				append(firstBlockCells, secondBlockCells...),
				nil,
			}

			if step.IsUseful(grid) {
				select {
				case results <- step:
				case <-done:
					return
				}
			}
		}

	}
}

func (self *blockBlockInteractionTechnique) Description(step *SolveStep) string {
	if len(step.TargetNums) != 1 {
		return ""
{'fraction_non_alphanumeric': 0.1127659574468085, 'fraction_numerical': 0.006524822695035461, 'mean_word_length': 3.859407305306685, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 3, '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': '1830022', 'n_tokens_mistral': 2707, 'n_tokens_neox': 2252, 'n_words': 597}