| Bytes | Lang | Time | Link |
|---|---|---|---|
| 420 | Bespoke | 250126T010338Z | Josiah W |
| 042 | Retina 0.8.2 | 250122T205800Z | Unrelate |
| 043 | 6502 Assembly | 250124T153904Z | testc |
| 009 | Jelly | 240517T124510Z | Jonathan |
| 049 | Perl 5 Minteger pF | 240517T214017Z | Xcali |
| 012 | Uiua | 250122T131409Z | nyxbird |
| 1646 | ☾ | 250122T075728Z | Ganer |
| 113 | AWK | 240521T194808Z | C K |
| 1134 | Cubical Agda | 240521T125527Z | Naï |
| 050 | Python 3.8 prerelease | 240517T075905Z | Jitse |
| 011 | 05AB1E | 240517T124333Z | Kevin Cr |
| 009 | 05AB1E | 240521T025644Z | alephalp |
| 013 | MATL | 240518T112436Z | Luis Men |
| 100 | Google Sheets | 240518T140214Z | doubleun |
| 038 | JavaScript ES6 | 240517T101405Z | Arnauld |
| 070 | Retina 0.8.2 | 240517T155751Z | Neil |
| 022 | Charcoal | 240517T151233Z | Neil |
| 6766 | Rust | 240517T092316Z | mousetai |
Bespoke, 425 420 bytes
-5 bytes by replacing DO P DO P with H SV. (Nice.)
each a/b/c point is on this chained triplet
so each node from it can go within triangle shape
circular motions,they may increase degree
leftward turnings decrease this
we notice while we move a-b-c,then go to start,stopping at degrees=one
i go:if move isnt a-b-c-to-origin,movement is by zero
counting each inputted position by diff is adequate
rounding direction=zero for dividing difference sequence,producing degree D
Keeps track of a running total for the motion between vertices (1 = clockwise, -1 = counterclockwise, 0 = no motion), and divides the result by 3 (rounded towards 0).
The fact that the result needs to be rounded towards 0 caused me some trouble. What I ended up doing was calculating a "sign" value \$s\$ as \$2 \cdot (0 < d) - 1\$, and calculating \$(sd \div 3) \cdot s\$. Bespoke is a stack-based language, so this required some juggling.
Retina 0.8.2, 47 42 bytes
+`(.).?\1
$1
1`ba|cb|ac
-$&
\w(...)*.*
$#1
I knew something like this should be possible and competitive--it just didn't occur to me that it would be in Retina!
Explanation:
+`(.).?\1
$1
Match any character, zero or one other characters, and a copy of that first character. Replace all of that with just the first character, and repeat until nothing changes.
Each iteration essentially "straightens out" every pair of steps that undo each other on top of gradually condensing runs of the same vertex, and repeating this eventually leaves only steps which are all in one direction or the other.
1`ba|cb|ac
-$&
Match the first occurrence of a counterclockwise pair and prepend a minus to it. Due to the previous step, if there's a counterclockwise pair anywhere in the string there has to be one at the very beginning, and this is sufficient to determine the sign of the result (unless it's 0, which can also be output with a leading minus--I assume this is fine).
\w(...)*.*
$#1
Match the first letter (i.e. after the sign if present), and replace that and everything after it with the length of everything strictly after it floor divided by 3. Since every step is in the same direction now, every three steps completes a full revolution, so the number of thirds-of-revolutions is just the number of steps which is 1 less than the number of letters.
Retina 0.8.2, 63 59 bytes
(.)\1*
$1$1
ba|cb|ac|$
--
O`.
+`-\w
^(((-)|.){6})*.*
$3$#1
...I was going to suggest this as a golf to Neil's Retina 0.8.2 solution, given that that is what it started as, until I realized I'd made some or another meaningful change on all but two lines (now one). Funny how that happens.
Explanation:
(.)\1*
$1$1
Replace every run of identical characters (including trivial runs of a single unique character) with precisely two copies of that character. This results in non-overlapping pairs corresponding to every overlapping pair in the original input, with an extra character at the beginning and end.
ba|cb|ac|$
--
Replace every counterclockwise pair, and the empty match at the end of the string, with two minuses. Each counterclockwise pair needs to contribute two minuses since each clockwise pair contributes two letters, and the extra two at the end balance out the leading and trailing unpaired letters.
O`.
+`-\w
Sort that result, so that minuses precede letters, then delete minus-letter pairs iteratively until none remain. Sorting is 1 byte shorter than matching unequal pairs in both orders and repeating \w.
^(((-)|.){6})*.*
$3$#1
Divide by 6 rounded towards 0, and convert to decimal:
Like Neil's paired division and conversion, this doesn't directly "do signed math" so much as it floor divides the nonnegative unary and copies the unary digit onto the beginning of the decimal result to serve as its sign. However, using a number-of-matches substitution $#{...} instead of a length substitution $.{...}, it's a little trickier to actually match specifically a minus sign without consuming it out of the total--I have to clumsily use ((-)|.) to include it, since filtering letters out afterwards is much bulkier (because decimal digits are also matched by \w),
Outside of the sign logic, this matches the entire string as 0 or more greedy repetitions of 6 of anything then 0 or more repetitions of anything at all (anchored to the start of the string to eliminate an extra empty match at the end), and replaces it with (the sign prefixed to) the number of times the 6-wide group matched in decimal.
6502 Assembly, 43 Bytes
Reads the stack as a string, calculates degree into A
ldx #$00
stx $00 ;counter: start at 0 rounds
inx
stx $02 ;bit mask for the 'bit' instruction
pla ;load first letter from stack
sta $01 ;previous letter: set to first letter
loop:
tax ;copy letter to X
sec
sbc $01 ;compare to previous letter by calculating their difference
beq skip ;skip repeated letters
stx $01 ;save current letter
clc
bit $02 ;bit test difference with the value in $02 (#$01)
bne not_c_a ;If the first bit of the difference was set, skip inversion
eor #$ff ;for a-c or c-a, invert difference
adc #$01 ;difference is still either -2 or 2, avoiding having to divide by 3 later
not_c_a :
adc $00 ;add difference to counter
sta $00 ;save counter
skip:
pla ;load next letter from stack
tsx ;did we hit the bottom?
bne loop ;continue loop
lda $00
cmp #$80 ;done, divide counter by 4
ror a
cmp #$80
ror a ;A contains the result!
Assembled:
A2 00 86 00 E8 86 02 68 85 01 AA 38 E5 01 F0 0F
86 01 18 24 02 D0 04 49 FF 69 01 65 00 85 00 68
BA D0 E7 A5 00 C9 80 6A C9 80 6A
Jelly, 12 9 bytes
IÆTṠS÷3r`
A monadic Link that accepts a list of the walked vertices' ASCII codes/byte values and yields a singleton list containing the net number of rounds.
Try it online! Or see the test-suite.
How?
The ASCII codes of the characters in the input are [97, 98, 99] for "abc", respectively.
The instructions are the substrings of length two of the input.
Each instruction has an expected direction, \$1\$ for clockwise, \$-1\$ for anticlockwise, or \$0\$ for no movement.
For most instructions, the direction is just the forward difference of the instruction's values (\$d\$ below), the only time this is not the case is when the ordinals have an absolute difference of two. The code coerces these by taking the sign of the tangent of the value in radians.
| instruction | expected direction | \$d\$ | \$\tan{d}\$ | \$v = \operatorname{sgn}(\tan d)\$ |
|---|---|---|---|---|
| ab | clockwise (\$1\$) | $$98-97=1$$ | \$1.557...\$ | \$1\$ |
| bc | clockwise (\$1\$) | $$99-98=1$$ | \$1.557...\$ | \$1\$ |
| ca | clockwise (\$1\$) | $$97-99=-2$$ | \$2.185...\$ | \$1\$ |
| cb | anticlockwise (\$-1\$) | $$98-99=-1$$ | \$-1.557...\$ | \$-1\$ |
| ba | anticlockwise (\$-1\$) | $$97-98=-1$$ | \$-1.557...\$ | \$-1\$ |
| ac | anticlockwise (\$-1\$) | $$99-97=2$$ | \$-2.185...\$ | \$-1\$ |
| aa | no movement (\$0\$) | $$97-97=0$$ | \$0\$ | \$0\$ |
| bb | no movement (\$0\$) | $$98-98=0$$ | \$0\$ | \$0\$ |
| cc | no movement (\$0\$) | $$99-99=0$$ | \$0\$ | \$0\$ |
IÆTṠS÷3r` - Link: list of character ordinals from [97,98,99] (i.e. "abc")
I - forward differences
ÆT - tan {that} (vectorises)
Ṡ - sign {that} (vectorises)
S - sum {that}
÷3 - divide {that} by three -> FractionalRounds
r` - inclusive range with self -> [int(FractionalRounds)]
Perl 5 -Minteger -pF, 49 bytes
map$\+=(0,-1,1,-1,1)[(ord)-ord$F[++$i]],@F}{$\/=3
Perl 5 -M5.010 -MList::Util=reduce -Minteger -pF, 53 bytes
reduce{$_=(ord$b)-ord$a;$\+=/2/?$_/-2:$_;$b}@F}{$\/=3
Uiua, 12 bytes
⌊÷3/+⍜+₁◿₃⧈-
⌊÷3/+⍜+₁◿₃⧈-
⧈- # deltas
⍜+₁◿₃ # mod 3 under +1 (results in -1, 0, or 1)
/+ # summed
⌊÷3 # floor divided by 3
☾, 16 Chars (46 bytes)
ᴙᙧ1ꟿⴵ○○-⨁→⹏3
(Note: ☾ uses a custom font, you can copy-paste the above code into the Web UI)
AWK, 113 bytes
{for(j=1;++i<length($0);){x=substr($0,i,2);x~"ab"||x~"bc"||x~"ca"?j++:0;x~"cb"||x~"ac"||x~"ba"?j--:0}}$0=int(j/3)
This will implicitly ignore double characters. I feel like there's a shorter trick somewhere, but my test haven't panned out.
Cubical Agda, 1134 bytes
Uses Agda 2.6.4.3, the cubical library 0.7 and the standard library 2.0.
{-# OPTIONS --cubical #-}
open import Cubical.Data.Int
open import Cubical.Data.Int.Divisibility
open import Cubical.Data.Nat
open import Cubical.Foundations.Everything
open import Data.List
open import Data.List.NonEmpty as L⁺
data 𝟛 : Type where
a b c : 𝟛
data △ : Type where
inc : 𝟛 → △
ab : inc a ≡ inc b
bc : inc b ≡ inc c
ca : inc c ≡ inc a
infix 40 _⇒_
_⇒_ : (x y : 𝟛) → inc x ≡ inc y
a ⇒ a = refl
a ⇒ b = ab
a ⇒ c = sym ca
b ⇒ a = sym ab
b ⇒ b = refl
b ⇒ c = bc
c ⇒ a = ca
c ⇒ b = sym bc
c ⇒ c = refl
🏃 : ∀ s → inc (L⁺.head s) ≡ inc (L⁺.last s)
🏃 s with snocView s
... | ys ∷ʳ′ y = go ys
where
go : ∀ ys → inc (L⁺.head (ys L⁺.∷ʳ y)) ≡ inc y
go [] = refl
go (v ∷ vs) = v ⇒ _ ∙ go vs
🧬 : △ → Type
🧬 (inc _) = ℤ
🧬 (ab i) = sucPathℤ i
🧬 (bc i) = sucPathℤ i
🧬 (ca i) = sucPathℤ i
_/3 : ℤ → ℤ
n@(pos _) /3 = quotRem 3 n (snotz ∘ injPos) .QuotRem.div
n@(negsuc _) /3 = - quotRem 3 (- n) (snotz ∘ injPos) .QuotRem.div
d : List⁺ 𝟛 → ℤ
d s = subst 🧬 (🏃 s) 0 /3
A straightforward winding number computation, slightly complicated by the fact that the string doesn't have to start and end on the same character.
We define the triangle as a higher inductive type generated by three points and three paths, as well as a covering space with fibre ℤ that looks like a "triple helix":
To get the degree/winding number of a string of vertices, we transport 0 along a path in the triangle generated by taking the "shortest" path from each vertex to the next, and divide the resulting integer by 3 towards zero.
There is no compiler for Cubical Agda (yet!), but we can check that the test cases compute correctly:
open import Agda.Builtin.Char
open import Agda.Builtin.FromString
open import Agda.Builtin.String
open import Data.Bool
open import Data.List.Effectful as List
open import Data.Maybe
open import Data.Maybe.Effectful as Maybe
parse1 : Char → Maybe 𝟛
parse1 'a' = just a
parse1 'b' = just b
parse1 'c' = just c
parse1 _ = nothing
parse : List Char → Maybe (List⁺ 𝟛)
parse s = mapA parse1 s >>= fromList
where open List.TraversableA Maybe.applicative
instance
IsString-vertices : IsString (List⁺ 𝟛)
IsString-vertices .IsString.Constraint s = T (is-just (parse (primStringToList s)))
IsString-vertices .IsString.fromString s ⦃ j ⦄ = to-witness-T _ j
_ : d "abca" ≡ 1
_ = refl
_ : d "bcab" ≡ 1
_ = refl
_ : d "cabc" ≡ 1
_ = refl
_ : d "accbbbaa" ≡ -1
_ = refl
_ : d "abcacba" ≡ 0
_ = refl
_ : d "abcabcab" ≡ 2
_ = refl
_ : d "abababababababcababababababab" ≡ 1
_ = refl
_ : d "abcbca" ≡ 1
_ = refl
_ : d "abcabcacb" ≡ 1
_ = refl
Python 3.8 (pre-release), 50 bytes
lambda a,*s:int(sum(-(a+~(a:=b))%3-1for b in s)/3)
-5 bytes thanks to Neil
-5 bytes thanks to Albert.Lang
Port of Arnauld's JavaScript answer.
Alternative solution:
Python 3, 69 bytes
lambda s:int(sum('aabbccabca'.count(x+y)-1for x,y in zip(s,s[1:]))/3)
05AB1E, 13 11 bytes
ÇÔ¥3%É·<O3÷
-2 bytes porting @LuisMendo's MATL answer, so make sure to upvote that answer as well!
Input as a string.
(If we're allowed to take the input as a list of codepoint-integers, the leading Ç can be removed for -1 byte.)
Try it online or verify all test cases.
Explanation:
Ç # Convert the (implicit) input-string to a list of codepoint-integers
Ô # Connected uniquify it to remove non-moving cases
¥ # Pop and get the forward-differences (deltas) of this list
3% # Modulo-3 each difference
É # Modulo-2 each of those
·< # Double and subtract 1 to convert the 0s to -1s (and 1s remain 1s)
O # Sum this list of 1s and -1s
3÷ # Integer-divide it by 3
# (after which the result is output implicitly)
05AB1E, 9 bytes
Ç¥>3%<O3÷
Ç¥>3%<O3÷
Ç # Convert each element in the input to its ASCII value
¥ # Calculate the deltas
> # Increment each element by 1
3% # Take each element modulo 3
< # Decrement each element by 1
O # Sum the elements
3÷ # Integer divide by 3
MATL, 13 bytes
dXzI\oEqsI/Zo
Try it online! or verify all test cases.
Explanation
d % Implicit input. Consecutive differences (of ASCII codes)
Xz % Remove zeros
I\ % Modulo 3 (element-wise)
o % Modulo 2 (element-wise)
E % Times 2 (element-wise)
q % Minus 1 (element-wise)
s % Sum of all values
I/ % Divide by 3
Zo % Round towards 0. Implicit display
Google Sheets, 100 bytes
=sort(let(a,mid(A1,row(A:A),2),int(sum(countif(a,{"ab","bc","ca"})-countif(a,{"ba","cb","ac"}))/3)))
Put the string in cell A1 and the formula in B1.
Counts instances of advancing and regressing letter pairs and divides by 3 à la Jonathan Allan's answer.
JavaScript (ES6), 38 bytes
Straightforward formula
Expects an array of ASCII codes.
a=>a.map(t=c=>t=-~t-(a+4-[a=c])%3)|t/3
JavaScript (ES6), 39 bytes
Chained modulos on ASCII code concatenation
Expects an array of ASCII codes.
a=>a.map(t=c=>t=~-t+(a+[a=c])%80%3)|t/3
Method
Move types are identified by computing the concatenation of the ASCII codes of the previous and current characters and reducing it modulo \$80\$ and modulo \$3\$. This gives \$0\$ for counter-clockwise, \$1\$ for no move and \$2\$ for clockwise.
| previous char. | current char. | concat. of ASCII codes | mod 80 | mod 3 | move type |
|---|---|---|---|---|---|
| a | a | 9797 | 37 | 1 | no move |
| a | b | 9798 | 38 | 2 | clockwise |
| a | c | 9799 | 39 | 0 | counter-clockwise |
| b | a | 9897 | 57 | 0 | counter-clockwise |
| b | b | 9898 | 58 | 1 | no move |
| b | c | 9899 | 59 | 2 | clockwise |
| c | a | 9997 | 77 | 2 | clockwise |
| c | b | 9998 | 78 | 0 | counter-clockwise |
| c | c | 9999 | 79 | 1 | no move |
Search code
The following code was used to brute-force the modulo values.
let A = [ 97, 98, 99 ];
for(let m0 = 1; m0 < 1000; m0++) {
for(let m1 = 1; m1 <= m0; m1++) {
if(A.every(p =>
A.every(c => {
let r = p - c ? c == p + 1 || c == 97 && p == 99 ? 1 : -1 : 0;
let v = (p + [c]) % m0 % m1 - 1;
return v == r;
})
)) {
console.log(m0, m1);
}
}
}
Retina 0.8.2, 70 bytes
(.)\1+
$1
\B
#
+T`#`-`b#a|c#b|a#c
+`\w|#-|-#
..?(.)?
$1
^(-)?.*
$1$.&
Try it online! Link includes test cases. Explanation:
(.)\1+
$1
Deduplicate consecutive letters.
\B
#
Insert #s between pairs of letters.
+T`#`-`b#a|c#b|a#c
Change #s to -s where the letters are anticlockwise.
+`\w|#-|-#
Delete the letters and cancel out adjacent #s and -s.
..?(.)?
$1
Integer divide by 3.
^(-)?.*
$1$.&
Convert to decimal.
Charcoal, 22 bytes
﹪%d∕ΣEΦθκ⊖﹪⊕⁻℅ι℅§θ곦³
Try it online! Link is to verbose version of code. Explanation:
θ Input string
Φ κ Filter out first letter
E Map over remaining letters
ι Current letter
℅ Ordinal
⁻ Minus
§θκ Previous letter
℅ Ordinal
⊕ Incremented
﹪ Modulo
³ Literal integer `3`
⊖ Decremented
Σ Take the sum
∕ Divide by
³ Literal integer `3`
﹪ Format as string using
%d Truncate to integer
Implicitly print
17 bytes if (as in all of the examples so far) the input can be assumed to start with a:
FSM⊖﹪⁻℅ιⅈ³→﹪%d∕ⅈ³
Try it online! Link is to verbose version of code. Explanation:
FS
Loop over the characters of the input string.
M⊖﹪⁻℅ιⅈ³→
Subtract the current position from the character's ordinal, reduce modulo 3, subtract 1, and add that to the current position.
﹪%d∕ⅈ³
Truncating divide the current position by 3.
Rust, 67 66 bytes
|a|a[1..].iter().fold((0,a[0]%3),|(b,c),d|(b-(c+!d)%3-1,*d%3)).0/3
Now handles repeats



