Last active
April 8, 2025 14:21
-
-
Save maxint137/c380678630730494376459975633e7bf to your computer and use it in GitHub Desktop.
assist with chess studying
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Flip the board | |
// @namespace http://tampermonkey.net/ | |
// @version 2024-04-06 | |
// @description try to take over the world! | |
// @author You | |
// @match https://www.chess.com/game/live/* | |
// @icon https://www.google.com/s2/favicons?sz=64&domain=chess.com | |
// @grant none | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Find button by role and accessible name | |
function getByRole(role, options = {}) { | |
// Get all elements with the specified role | |
const elements = Array.from(document.querySelectorAll(`[role="${role}"], ${role}`)); | |
// Filter by accessible name if specified | |
if (options.name) { | |
return elements.find(element => { | |
return element.textContent.includes(options.name) || | |
element.getAttribute('aria-label') === options.name; | |
}); | |
} | |
return elements[0]; | |
} | |
setTimeout(() => { | |
const [ , paramString ] = window.location.href.split( '?' ); | |
// Use the function to find and click the button | |
const flipButton = getByRole('button', { name: 'Flip Board' }); | |
if (new URLSearchParams( paramString ).get('flip') && flipButton) { | |
flipButton.click(); | |
} | |
// const ply = Number(new URLSearchParams( paramString ).get('ply')); | |
// document.querySelectorAll(`[data-whole-move-number='${Math.floor(ply/2)+1}']`)[0].children[(ply%2)*2].dispatchEvent(new PointerEvent('pointerdown')); | |
document.getElementsByClassName('board-modal-header-close game-over-header-close')[0].click(); | |
}, "3000"); | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import re | |
import sys | |
from scoutfish import Scoutfish | |
def find_link(text): | |
pattern = r'\[Link "(.*?)"\]' | |
matches = re.findall(pattern, text) | |
return matches[0] | |
def extract_fen_components(fen): | |
""" | |
Extract active color, halfmove clock, and fullmove number from a FEN string. | |
Args: | |
fen (str): The FEN string representing a chess position | |
Returns: | |
tuple: (active_color, halfmove_clock, fullmove_number) | |
""" | |
# Split the FEN string into its components | |
components = fen.split() | |
# Check if the FEN has all required components | |
if len(components) < 6: | |
raise ValueError("Invalid FEN string: missing components") | |
# Extract the values | |
active_color = components[1] # 'w' for White, 'b' for Black | |
halfmove_clock = int( | |
components[4] | |
) # Number of halfmoves since last capture or pawn advance | |
fullmove_number = int(components[5]) # The number of the full move | |
return active_color, halfmove_clock, fullmove_number | |
def extract_players(pgn_string): | |
pattern = r'\[White "(.*?)"\]\s*\[Black "(.*?)"\]' | |
match = re.search(pattern, pgn_string) | |
return match.groups() if match else (None, None) | |
def extract_move(pgn_string, move_number): | |
""" | |
Extracts the specified move number (White and Black) from a PGN string. | |
Args: | |
pgn_string: The PGN string of the chess game. | |
move_number: The move number to extract (e.g., 5 for move #5). | |
Returns: | |
A tuple containing White's move and Black's move as strings, or | |
(None, None) if the move number is not found. | |
""" | |
# Regular expression to find the move number and the subsequent moves | |
# This regex looks for the move number followed by a dot (.), then captures | |
# White's move and Black's move (if it exists). It accounts for variations | |
# in PGN formatting (e.g., annotations, comments). | |
# Clean up the PGN text by removing clock annotations | |
pattern = rf"{move_number}\.\s+(\S+)\s+(?:{move_number}\.{{3}}\s+)?(\S+)" | |
cleaned_pgn = re.sub(r"\{[^}]*\}", "", pgn_string) | |
match = re.search(pattern, cleaned_pgn) | |
if match: | |
white_move = match.group(1) | |
black_move = ( | |
match.group(2) if match.group(2) else None | |
) # Black's move might be missing at the end | |
return white_move, black_move | |
else: | |
return None, None # Move number not found | |
p = Scoutfish() | |
p.setoption("threads", 4) # Will use 4 threads for searching | |
p.open(sys.argv[1]) | |
fen0 = sys.argv[2] if 3 <= len(sys.argv) else None | |
while True: | |
fen = fen0 or input("FEN:") | |
fen0 = None | |
if not fen: | |
break | |
q = {"sub-fen": fen} | |
result = p.scout(q) | |
num = result["match count"] | |
games = p.get_games(result["matches"]) # Load the pgn games from my_big.pgn | |
ac, hm, fm = extract_fen_components(fen) | |
data = [] | |
for g in games: | |
white, black = extract_players(g["pgn"]) | |
wm0, black_move = extract_move(g["pgn"], fm) | |
wm1, _bm = extract_move(g["pgn"], fm + 1) | |
white_move = wm0 if "w" == ac else wm1 | |
data.append( | |
{ | |
"ac": ac, | |
"white_move": white_move, | |
"black_move": black_move, | |
"url": f"{find_link(g['pgn'])}?move={g['ply'][0]}{'&flip=da' if black=='redwinereduction' else ''}", | |
} | |
) | |
# print( | |
# f'[{white_move if "w" == ac else "_":^3}, {black_move if "b" == ac else "_":^3}]', | |
# f"{find_link(g['pgn'])}?move={g['ply'][0]}{'&flip=da' if black=='redwinereduction' else ''}", | |
# ) | |
# This method doesn't require sorting first | |
grouped = {} | |
for obj in data: | |
key = obj["white_move"] if "w" == ac else obj["black_move"] | |
if key not in grouped: | |
grouped[key] = [] | |
grouped[key].append(obj) | |
sorted_items = sorted(grouped.items(), key=lambda values: -len(values[1])) | |
for key, values in sorted_items: | |
values.sort(key=lambda x: x["white_move"] if "b" == ac else x["black_move"]) | |
print(f"[{key:^3},.." if "w" == ac else f".., {key:^3}]") | |
for obj in values: | |
print( | |
f" {obj["black_move"]:^3}] {obj['url']}" | |
if "w" == ac | |
else f"[{obj["white_move"]:^3},.. {obj['url']}" | |
) | |
print("Found " + str(num) + " games") | |
p.close() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment