# Determine the Octet(/Duplet/18-electron) Rule

Given an atomic number in the inclusive range [1,118], write the shortest program/function that outputs one of {2,8,18} if the element with that atomic number follows the duplet, octet, or 18-electron rule, respectively.

### Notes

• The Octet/Duplet/18-electron rule is a rule of thumb that describes how many electrons is needed for an element to completely fill their outer electron shells.
• The Duplet rule only applies to H, He, and Li (atomic numbers 1, 2, and 3)
• The 18-electron rule applies to all transition metals (21-30, 39-48, 72-80, 104-112), lanthanides (57-71), and actinides (89-103).
• The Octet rule applies to everything else (4-20, 31-38, 49-56, 81-88, 113-118).

### Test Cases

input => output
1 => 2
2 => 2
3 => 2
4 => 8
18 => 8
19 => 8
20 => 8
21 => 18
26 => 18
30 => 18
31 => 8
92 => 18
118 => 8

• Commented Feb 10, 2023 at 16:26
• I think there's a typo - it says the Octet rule applies to "133-118", it should be "113-118" Commented Feb 10, 2023 at 17:39
• @TheThonnu fixed Commented Feb 10, 2023 at 17:40
• As a former chemistry major, I'd like to point out that the "octet" elements are those of the last 6 columns of the periodic table, wrapping round to the first 2 columns, to form runs of 8 elements. Element 4 Beryllium would then fit as a duplet, but in reality Be2+ in water borrows back 8 electrons from H2O to form Be(4*H2O)2+ to become like neon, so Wikipedia calls it an additional octet element. As for the other elements being 18-electron: the actual situation with lanthanides and actinides is way more complex but changing the rules of the question at this stage will render answers invalid Commented Feb 10, 2023 at 21:28

# Excel, 60 bytes

=IFNA(8+10*ISEVEN(MATCH(A1,{4,21,31,39,49,57,81,89,113})),2)


Input in cell A1.

# Charcoal, 22 bytes

Ｉ⊗§”|↖⁵Z·⁰χ⦄P↑mＭρbl∨”Ｎ


Try it online! Link is to verbose version of code. Explanation: Most of the program is simply a compressed look-up table.

   ”...”    Compressed look-up table
§         Indexed by
Ｎ   Input as a number
⊗          Doubled
Ｉ           Cast to string
Implicitly print


# Vyxal, 19 18 bytes

»µ7!¾∷»₄τ¦>ḣ∑∷+›²d


-1 byte by porting part of Jonathan Allan's Jelly answer.

»...»               # push compressed integer
₄τ             # convert to base 26
¦            # cumulative sum
this results in the list [3,20,30,38,48,56,80,88,112]
>           # is less than input?
ḣ          # extract head, pushes a[0] and a[1:] (a[0] == 0 iff input is less than 4)
∑         # sum
∷        # modulo 2 (this is 1 iff the 18-electron rule applies)
›      # increment
²     # square
d    # double


# Ruby, 42 bytes

->m{m>3?8+5*(2&70352637804063<<-m/2+12):2}


Try it online!

Unfortunately this fairly unimaginative approach works best.

Ruby rounds toward -infinity, so the minus sign in -m/2 ensures that transitions occur in the right place. This expression evaluates to -10 for both 19 and 20 input, and -11 for both 21 and 22 input. To get the first 18 output, we take this -11 and add 12 for an overall shift of +1. This gives a final answer of 8+5*(2) which is 18. Larger numbers give negative leftshift, which Ruby interprets as positive rightshift.

the magic number can be replaced by 0x3ffc3ffc3e1f which is the same number of bytes. If three leading zeroes are added to the hex number, +12 can be deleted, but it's the same number of bytes.

# Ruby, 65 bytes

->m{n=0
" $(1:JZ".bytes{|i|n+=15<<i} m>3?18-10*(1&n>>(m+67)/2):2}  Try it online! • Your 54-byte solution is actually 44 since it contains 10 trailing spaces... Commented Feb 11, 2023 at 1:51 • @dingledooper thanks. It's 42 bytes now. Commented Feb 11, 2023 at 2:22 # Jelly, 19 bytes >“Þœ&08PXp“¤‘§ḂS‘²Ḥ  A monadic Link that accepts an integer from $$\[1,118]\$$ and yields the rule, $$\2\$$, $$\8\$$, or $$\18\$$. Try it online! ### How? >“...“¤‘§ḂS‘²Ḥ - Link: Atomic number, A e.g. 48 “...“¤‘ - lists of code-page indices -> [[20,30,38,48,56,80,88,112],[3]] > - (A) greater than? (that) [[ 1, 1, 1, 0, 0, 0, 0, 0],[1]] § - sums [3,1] Ḃ - mod two [1,1] S - sum 2 ‘ - increment 3 ² - square 9 Ḥ - double 18  # Python, 72 71 56 55 bytes lambda n:(n<4or sum(i>n for i in b"\x04\x15\x1f'19QYq")%2*5+4)*2  Attempt This Online! (But the \x04\x15\x1f should be replaced with the actual unprintable characters) Based off Jos Woolley's Excel answer. -15 thanks to dingledooper -1 thanks to pan ## Python, 66 bytes lambda n:(n<4)*2+((20<n<31)+(38<n<49)+(56<n<81)+(88<n<113))*18or 8  Attempt This Online! • In the second answer, you can save a bunch by using a byte string instead of an array. Commented Feb 10, 2023 at 20:00 • @dingledooper thanks! I didn't think of that. Commented Feb 10, 2023 at 20:11 • 55 – pan Commented Feb 18, 2023 at 4:38 # 05AB1E, 20 bytes 49Þ1š•N‚Ÿи––•₂вÅΓ·Iè  Port of @Neil's Charcoal answer. A port of @AndrovT's Vyxal answer is 20 bytes as well: •¶sWü„•₂вηO›ćsOÉ6«*Ì  Make sure to upvote both @Neil's and @AndrovT's answers as well! Explanation: 49 # Push 49 Þ # Cycle it indefinitely: [4,9,4,9,4,9,...] 1š # Prepend a 1: [1,4,9,4,9,4,9,...] •N‚Ÿи––• # Push compressed integer 25350987139094 ₂в # Convert it to base-26 as list: [4,17,10,8,10,8,24,8,24,6] ÅΓ # Run-length decode the two lists: [1,1,1,1,4,4,4,...] · # Double each: [2,2,2,2,8,8,8,...] Iè # (0-based) index the input into it # (after which the result is output implicitly) •¶sWü„• # Push compressed integer 766210902312 ₂в # Convert it to base-26 as list: [3,17,10,8,10,8,24,8,24] η # Get all prefixed of this list: [[3],[3,17],[3,17,10],...] O # Sum each inner prefix: [3,20,30,38,48,56,80,88,112] › # Check if the (implicit) input-integer is larger than the values ć # Extract head; push first item and remainder-list separately s # Swap so the remainder-list is at the top of the stack O # Sum it É # Modulo-2 (with an is_odd check) 6« # Append a 6 to this 0 or 1 * # Multiply it to the extracted head Ì # Increase it by 2 # (after which the result is output implicitly)  See this 05AB1E tip of mine (sections How to compress large integers? and How to compress integer lists?) to understand why •N‚Ÿи––• is 25350987139094; •N‚Ÿи––•₂в is [4,17,10,8,10,8,24,8,24,6]; •¶sWü„• is 766210902312; and •¶sWü„•₂в is [3,17,10,8,10,8,24,8,24]. # JavaScript (ES6), 42 bytes A hash formula with a 32-bit lookup. n=>n<4?2:60620415>>(-~n>>1)*223%511&1?18:8  Try it online! # JavaScript (ES6), 47 bytes A longer but slightly more subtle method. n=>n<4?2:--n%16>="_468080"[n>>4]^n>29&n<32?18:8  Try it online! ### Method This is based on the pattern that appears when the atomic numbers are arranged by groups of $$\16\$$, starting at $$\0\$$ instead of $$\1\$$. We use the following condition to determine whether it's 18-electon or octet: $$(n \bmod 16)\ge T_{\lfloor n/16 \rfloor}$$ There's unfortunately an exception for $$\n=30\$$ and $$\n=31\$$ (atomic numbers $$\31\$$ and $$\32\$$) which is not golfed very efficiently, making this method overall less competitive than the one using a more basic hash. In the diagram below, we use +, . and x for duplet, octet and 18-electron respectively.  | 0 1 2 3 4 5 6 7 8 9 A B C D E F ---+-------------------------------- 00 | + + + . . . . . . . . . . . . . 10 | . . . . x x x x x x x x x x . . 20 | . . . . . . x x x x x x x x x x 30 | . . . . . . . . x x x x x x x x 40 | x x x x x x x x x x x x x x x x 50 | . . . . . . . . x x x x x x x x 60 | x x x x x x x x x x x x x x x x 70 | . . . . . .  # C (GCC), 81767168 57 bytes thanks neil for -11 bytes f(x){x=x<4?2:0x1fffffe01fffffe01ff807fell<<20>>x&1?18:8;}  Attempt This Online! Return 2 for <= 3, otherwise lookup the value in the 64-bit constants, where 0 is the 18 rule and 1 is the 8 rule. • Save 11 bytes by using a 128-bit integer constant: f(x){x=x<4?2:0x1fffffe01fffffe01ff807fell<<20>>x&1?18:8;} – Neil Commented Feb 10, 2023 at 22:55 # Pip, 61 47 41 bytes Ia<4P2EL{P%#({\a>a}FI(A*" &08PXp"))*t+8}  Attempt This Online! -20 thanks to @DLosc #### Explanation Ia<4 # if the input is less than 4: P2 # print 2 EL{ # else: P # print the following: %#( # length mod 2 of: {\a>a} # is the input greater than this number? FI(A*"...") # filter the list [20,30,38,48,56,80,88,112] by the above )*t+8 # times 10 plus 8 } # enf if  Old: Ia<4 # if the input is less than 4: P2 # print 2 EL{ # else: Fi { # for i in ... A*"..." # the ASCII code points of the string # [20,30,38,48,56,80,88,112]: l:lAE # append the following to l (a>i) # is the input greater than i? } # end for P # print ... (1Nl) # ... the count of 1s in l ... % # ... mod 2 ... *t+8 # ... times 10 plus 8 } # end if  • Probably the shortest way to generate the list [20;30;38;48;56;80;88;112], if you don't mind a couple of unprintable characters, is to make an 8-character string with those codepoints and use A* to get the ASCII code of each character: ATO. l:lAE(a>i) can be lAE:a>i, but in this case I'd suggest instead filtering the list and taking the length of the result. Also, several of the curly braces and parentheses are unnecessary; try removing them one pair at a time and see if you get the same results. Commented Feb 17, 2023 at 23:06 • Oh, and t is initialized to 10, so that'll save a byte. Commented Feb 17, 2023 at 23:08 # Scala, 103 bytes Modified from @The Thonnu's answer. Golfed version. Try it online! def f(n:Int)=if(n<4)2 else(Seq[Byte](0x04,0x15,0x1f,0x27,0x31,0x39,0x51,0x59,0x71).count(_ >n)%2*5+4)*2  Ungolfed version. object PythonToScala { def f(n: Int): Int = { val b = Array[Byte](0x04, 0x15, 0x1f, 0x27, 0x31, 0x39, 0x51, 0x59, 0x71) if (n < 4) 2 else (b.count(_ > n) % 2 * 5 + 4) * 2 } def main(args: Array[String]): Unit = { for (i <- 1 to 118) { println(s"$i  \t-->\t  \${f(i)}")
}
}
}