2
\$\begingroup\$

Introduction

Windows 95 used a very simple algorithm for verifying license keys. The scheme of an OEM license key always looked like this:

xxxyy-OEM-NNNNNNN-zzzzz

The day can be anything within 001-366, and the year cannot be lower than 95 or above 03.

xxx represents the day of the year the key was printed on (for example 192 = 10th of July) and is a number between 001-366. yy represents the year the key was printed on and cannot be lower than 95 or above 03.

This means for example, a key with 19296 as the first segment was printed on 10th of July 1996.

The second segment is always OEM.

The third segment (NN..) is always starts with 00 and the sum of the digits has to be divisible by 7

The fourth segment is irrelevant, provided the field is the appropriate length, but has to be filled out with digits.

Challange

Given a string, output a truthy or falsy value whether this oem key is valid or not.

Rules

  • You may provide a function or a program
  • This is , so the submission with the least amount of bytes wins!

Test cases

Valid keys

12899-OEM-0042134-31009
15795-OEM-0001355-07757
35201-OEM-0009165-64718

Invalid keys

12804-OEM-0033143-31009
16595-OEM-0001695-9
12899-OEM-0022134-31009
27497-OEM-0525347-80387
\$\endgroup\$
  • 8
    \$\begingroup\$ Is 36695 a valid day/year combo? (knowing that 1995 is not a leap year) \$\endgroup\$ – Arnauld Jun 3 at 14:12
  • 2
    \$\begingroup\$ You probably should add some invalid keys where everything goes wrong, e.g. FOO$23-ABC. (Or, if this is an invalid test case, specify what exactly may go wrong.) \$\endgroup\$ – Arnauld Jun 3 at 14:19
  • 5
    \$\begingroup\$ Why is the title "Generate a valid key", but the challenge asks to check whether a key is valid? \$\endgroup\$ – the default. Jun 3 at 14:23
  • 5
    \$\begingroup\$ Windows 95 installer, 0 bytes \$\endgroup\$ – Adám Jun 3 at 14:38
  • 2
    \$\begingroup\$ Suggested invalid test cases: 12899-OEM-0042134-310099 and 012899-OEM-0042134-31009 \$\endgroup\$ – Noodle9 Jun 3 at 14:46
2
\$\begingroup\$

perl -pl -MList::Util=sum, 106 113 bytes

$_=/^(?!000)(([012]\d\d|3([0-5]\d|6[0-5]))(9[5-9]|0[0-3])|366(00|96))-OEM-00(\d{5})-\d{5}$/&&!(sum(split//,$6)%7)

Try it online!

This assumes an ASCII only input. If the input can be outside of ASCII, we'd have to add a single extra byte (a trailing a modifier on the main regexp). It also assumes a leading 366 can only occur in the years 96 and 00, them being leap years. The bulk of the matching is done by a regexp; testing of divisibility by 7 is done outside of it.

Reads keys from STDIN; writes 1 to STDOUT for valid keys, empty lines for invalid keys.

Edit: fixed the issue mentioned by @Dingus.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ @Dingus Fixed now. \$\endgroup\$ – Abigail Jun 3 at 16:27
  • \$\begingroup\$ Nice! I was trying to find a way to abuse -F again, but the Reged won't match in -F for some reason so I've given up! You can save some bytes using $6=~/./g instead of the split though! \$\endgroup\$ – Dom Hastings Jun 3 at 20:12
1
\$\begingroup\$

Ruby -n, 110 bytes

p~/^(\d{3})(9[5-9]|0[0-3])-OEM-(00\d{5})-\d{5}$/&&$3.to_i.digits.sum%7<1&&$1>?0*3&&$1<"36#{$2=~/6|0$/?7:6}"||p

Try it online!

Takes input on STDIN. Outputs true to STDOUT for valid keys and nil for invalid keys.

Uses three regex capture groups: $1 is xxx, $2 is yy, and $3 is NNNNNNN. First tests whether the key has the correct general format. If so, then tests whether the sum of digits in $3 is divisible by 7. Then tests whether $1 is lexicographically greater than '000' and less than either '366' (non-leap years) or '367' (leap years); this works because we know at this point that $1 is a string of three digits. Leap years occur when $2 contains 6 or ends with 0.

| improve this answer | |
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 73 85 78 bytes

•J" L•S£ε"©366L ₃Ƶ2Ÿт%y4Ö≠i®366Ê* '- …OEM '- 0мõ ÐþQ×SO7Ö1 '- ÐþQ×g5"#Nè.VQà}P

+12 bytes to account for leap years.

Will try to golf it down from here. If only 05AB1E had regex.. :/

Try it online or verify a lot more test cases.

Explanation:

•J" L•      "# Push compressed integer 321312516
      S      # Convert it to a list of digits: [3,2,1,3,1,2,5,1,6]
       £     # Split the (implicit) input-string into parts of that size
ε            # Map each part to:
 "©366L ₃Ƶ2Ÿт%y4Ö≠i®366Ê* '- …OEM '- 0мõ ÐþQ×SO7Ö1 '- ÐþQ×g5"
             #  Push this string
  #          #  Split the string on spaces
   Nè        #  Use the map-index to index into this string
     .V      #  Evaluate/execute it as 05AB1E code
       Q     #  Check that the top two values on the stack are equal
        à    #  Pop and take its maximum (no op if it's already a single 0/1 boolean)
}P           # After the map: check if all were truthy by taking the product
             # (after which the result is output implicitly)

As for each individual part:

             # xxx:
©            #  Store the string in variable `®` (without popping)
 366L        #  Push a list in the range [1,366]
             #  After the eval:
     Q       #   Check for each value whether it's equal to the current `xxx` part
      à      #   Check if any are truthy in this list by taking the maximum

             # yy:
₃            #  Push builtin 95
 Ƶ2          #  Push compressed integer 103
   Ÿ         #  Pop both an push a list in the range [95,103]
    т%       #  Take modulo-100 on each value
y            #  Push the current `yy` part again
 4Ö≠i        #  If it's NOT divisible by 4 (so neither 96 nor 00):
     ®       #   Push the `xxx` part from variable `®`
      366Ê   #   Check that it's NOT equal to 366
          *  #   And check if both are truthy by multiplying them together
    }        #  (implicit due to the eval: close the if-statement)
             #  After the eval:
     Q       #  Check for each value whether it's equal to the current `yy` part
      à      #  Check if any are truthy in this list by taking the maximum

             # OEM:
…OEM         #  Push string "OEM"
             #  After the eval:
    Q        #   Check if it's equal to the current `OEM` part
     à       #   No-op maximum, since the max digit of 0/1 is 0/1 respectively

             # 00:
0м           #  Remove all 0s from the current `00` part
  õ          #  Push an empty string ""
             #  After the eval:
   Q         #   Check if the `00` part with zeroes is equal to the empty string
    à        #   No-op maximum, since the max digit of 0/1 is 0/1 respectively

             # NNNNN:
Ð            #  Triplicate the current `NNNNN` part
 þ           #  Pop and only leave its digits
  Q          #  Check that it's equal to the `NNNNN` part (1 if truthy; 0 if falsey)
   ×         #  Repeat the part that many times (`NNNNN` if truthy; "" if falsey)
    S        #  Convert it to a list of characters
     O       #  Sum those digits together
      7Ö     #  Check that it's divisible by 7
        1    #  Push a 1
             #  After the eval:
         Q   #   Check whether the divisibility check is equal to the 1
          à  #   No-op maximum, since the max digit of 0/1 is 0/1 respectively

             # zzzzz:
ÐþQ×         #  Similar as above for NNNNN
    g        #  Pop and push its length
     5       #  Push a 5
             #  After the eval:
      Q      #   Check if the length is equal to the 5
       à     #   No-op maximum, since the max digit of 0/1 is 0/1  respectively

             # -:
'-          '#  Push a "-"
             #  After the eval:
  Q          #   Check if the `-` part is equal to the "-"
   à         #   No-op maximum, since the max digit of 0/1 is 0/1  respectively

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •J" L• is 321312516 and Ƶ2 is 103.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ 2000 was also a leap year \$\endgroup\$ – Jonathan Allan Jun 3 at 16:39
  • \$\begingroup\$ @JonathanAllan Fixed at no change in bytes. Thanks for noticing. \$\endgroup\$ – Kevin Cruijssen Jun 3 at 16:48

Not the answer you're looking for? Browse other questions tagged or ask your own question.