116 lines
4.1 KiB
Java
116 lines
4.1 KiB
Java
package puzzle;
|
|
|
|
import module java.base;
|
|
import module java.sql;
|
|
|
|
public final class ScoreHintsTask {
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
Class.forName("org.sqlite.JDBC");
|
|
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:tools/hint/hint.sqlite")) {
|
|
updateCrossScores(conn, ScoreHintsTask::crossabilityScore, 1000);
|
|
}
|
|
}
|
|
static final Map<Character, Integer> LETTER_WEIGHT = Map.ofEntries(
|
|
Map.entry('E', 10), Map.entry('N', 9), Map.entry('A', 9), Map.entry('R', 8),
|
|
Map.entry('I', 8), Map.entry('O', 7), Map.entry('S', 7), Map.entry('T', 7),
|
|
Map.entry('D', 6), Map.entry('L', 6), Map.entry('K', 5), Map.entry('M', 5),
|
|
Map.entry('U', 5), Map.entry('P', 4), Map.entry('G', 4), Map.entry('H', 4),
|
|
Map.entry('V', 4), Map.entry('B', 3), Map.entry('W', 3),
|
|
Map.entry('C', 2), Map.entry('F', 2), Map.entry('Z', 2),
|
|
Map.entry('J', 1), Map.entry('Y', 1), Map.entry('Q', 0), Map.entry('X', 0)
|
|
);
|
|
|
|
static boolean isVowel(char ch) {
|
|
return ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U';
|
|
}
|
|
|
|
static int crossabilityScore(String w) {
|
|
var score = 0;
|
|
var vowels = 0;
|
|
for (var i = 0; i < w.length(); i++) {
|
|
var ch = w.charAt(i);
|
|
score += LETTER_WEIGHT.getOrDefault(ch, 2);
|
|
if (isVowel(ch)) vowels++;
|
|
}
|
|
var ratio = vowels / (double) w.length();
|
|
if (ratio >= 0.35 && ratio <= 0.65) score += 8;
|
|
if (w.indexOf('Q') >= 0 || w.indexOf('X') >= 0) score -= 6;
|
|
if (w.indexOf('Y') >= 0 || w.indexOf('J') >= 0) score -= 2;
|
|
return score;
|
|
}
|
|
/**
|
|
* Updates hints.cross_score by computing a score from hints.word.
|
|
*
|
|
* @param conn open JDBC connection (PostgreSQL)
|
|
* @param scoreFn callback: scoreFn.applyAsInt(word)
|
|
* @param batchSize e.g. 1000
|
|
*/
|
|
public static void updateCrossScores(
|
|
Connection conn,
|
|
ToIntFunction<String> scoreFn,
|
|
int batchSize
|
|
) throws SQLException {
|
|
|
|
// Use a transaction for speed + consistency
|
|
final boolean prevAutoCommit = conn.getAutoCommit();
|
|
conn.setAutoCommit(false);
|
|
|
|
// Server-side cursor behavior in pgjdbc requires autoCommit=false + fetchSize>0
|
|
final String selectSql =
|
|
"SELECT id, puzzle_norm " +
|
|
"FROM hints " +
|
|
"WHERE puzzle_norm IS NOT NULL"; // optionally add: " AND cross_score IS NULL"
|
|
|
|
final String updateSql =
|
|
"UPDATE hints SET cross_score = ? WHERE id = ?";
|
|
|
|
try (PreparedStatement psSel = conn.prepareStatement(selectSql);
|
|
PreparedStatement psUpd = conn.prepareStatement(updateSql)) {
|
|
|
|
psSel.setFetchSize(batchSize);
|
|
|
|
int pending = 0;
|
|
|
|
try (ResultSet rs = psSel.executeQuery()) {
|
|
while (rs.next()) {
|
|
long id = rs.getLong("id");
|
|
String word = rs.getString("puzzle_norm");
|
|
|
|
int score;
|
|
try {
|
|
score = scoreFn.applyAsInt(word);
|
|
} catch (RuntimeException ex) {
|
|
ex.printStackTrace();
|
|
// If scoring fails, decide your policy: skip or set 0.
|
|
// Here: skip row.
|
|
continue;
|
|
}
|
|
|
|
psUpd.setInt(1, score);
|
|
psUpd.setLong(2, id);
|
|
psUpd.addBatch();
|
|
pending++;
|
|
|
|
if (pending >= batchSize) {
|
|
psUpd.executeBatch();
|
|
conn.commit();
|
|
pending = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pending > 0) {
|
|
psUpd.executeBatch();
|
|
conn.commit();
|
|
}
|
|
|
|
} catch (SQLException e) {
|
|
conn.rollback();
|
|
throw e;
|
|
} finally {
|
|
conn.setAutoCommit(prevAutoCommit);
|
|
}
|
|
}
|
|
}
|