awards
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package award
|
||||
|
||||
import "strings"
|
||||
|
||||
// wpxPrefix derives the CQ WPX prefix from a callsign. This is an approximation
|
||||
// of the official WPX rules — good enough to count distinct prefixes worked:
|
||||
// - standard call: letters+digits up to and including the LAST digit of the
|
||||
// first group (F4BPO→F4, EA8ABC→EA8, 9A1AA→9A1, OH2BH→OH2)
|
||||
// - no digit: first two letters + "0" (RAEM→RA0)
|
||||
// - portable "A/B": a short alpha(+digit) segment is treated as the prefix
|
||||
// designator; a lone-digit segment replaces the call's digit (F4BPO/9→F9)
|
||||
func wpxPrefix(call string) string {
|
||||
c := strings.ToUpper(strings.TrimSpace(call))
|
||||
if c == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(c, "/") {
|
||||
return portablePrefix(c)
|
||||
}
|
||||
return standardPrefix(c)
|
||||
}
|
||||
|
||||
func portablePrefix(c string) string {
|
||||
parts := strings.Split(c, "/")
|
||||
// Drop pure operating-modifier suffixes.
|
||||
kept := make([]string, 0, len(parts))
|
||||
for _, p := range parts {
|
||||
switch p {
|
||||
case "P", "M", "MM", "AM", "QRP", "A", "R", "B", "LH":
|
||||
continue
|
||||
}
|
||||
if p != "" {
|
||||
kept = append(kept, p)
|
||||
}
|
||||
}
|
||||
if len(kept) == 0 {
|
||||
kept = parts
|
||||
}
|
||||
// Pick a base = the longest segment (the actual call).
|
||||
base := kept[0]
|
||||
for _, p := range kept[1:] {
|
||||
if len(p) > len(base) {
|
||||
base = p
|
||||
}
|
||||
}
|
||||
for _, p := range kept {
|
||||
if p == base {
|
||||
continue
|
||||
}
|
||||
if isAllDigits(p) {
|
||||
// Lone digit replaces the call's region digit: F4BPO/9 → F9.
|
||||
return replaceLastDigit(standardPrefix(base), p)
|
||||
}
|
||||
if len(p) <= 4 && hasLetter(p) {
|
||||
// Prefix designator wins: VP8/F4BPO → VP8 (+digit if missing).
|
||||
return ensureTrailingDigit(p)
|
||||
}
|
||||
}
|
||||
return standardPrefix(base)
|
||||
}
|
||||
|
||||
// standardPrefix applies the basic WPX rule to a plain callsign: the prefix is
|
||||
// the call up to and including its last digit (9A1AA→9A1, EA8ABC→EA8). Standard
|
||||
// callsigns carry no digit in the suffix, so "last digit" is the prefix digit.
|
||||
func standardPrefix(c string) string {
|
||||
lastDigit := -1
|
||||
for i := 0; i < len(c); i++ {
|
||||
if c[i] >= '0' && c[i] <= '9' {
|
||||
lastDigit = i
|
||||
}
|
||||
}
|
||||
if lastDigit < 0 {
|
||||
// No digit at all: first two letters + 0.
|
||||
if len(c) >= 2 {
|
||||
return c[:2] + "0"
|
||||
}
|
||||
return c + "0"
|
||||
}
|
||||
return c[:lastDigit+1]
|
||||
}
|
||||
|
||||
func ensureTrailingDigit(p string) string {
|
||||
for i := 0; i < len(p); i++ {
|
||||
if p[i] >= '0' && p[i] <= '9' {
|
||||
return p
|
||||
}
|
||||
}
|
||||
return p + "0"
|
||||
}
|
||||
|
||||
func replaceLastDigit(prefix, digit string) string {
|
||||
for i := len(prefix) - 1; i >= 0; i-- {
|
||||
if prefix[i] >= '0' && prefix[i] <= '9' {
|
||||
return prefix[:i] + digit
|
||||
}
|
||||
}
|
||||
return prefix + digit
|
||||
}
|
||||
|
||||
func isAllDigits(s string) bool {
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < '0' || s[i] > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hasLetter(s string) bool {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if (s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user