16
\$\begingroup\$

Your goal is to find out the order of the drivers in a race (1st, 2nd, 3rd, etc) each time the start / finish line is crossed. This would allow someone to see the progression of a driver throughout the race, for example if they started in 3rd, then fell backwards to 5th halfway through the race, before moving back up to 4th and finishing there.

The only data you will have is a list of car numbers, in the order they cross the start / finish line. This will not be broken up by lap, and will not have any timestamps / any other data.

For example, in a 2 lap race with no overtaking, with a starting grid of cars 43, 60, 22, the input to your program would be 43, 60, 22, 43, 60, 22, 43, 60, 22.

Note that each number appears 3 times (even though there are 2 laps):

  • the cars start behind the line, and cross it as the race starts and they begin their first lap
  • they cross the line again at the end of the first lap
  • they cross the line for a final time at the end of the second lap (which is the end of the race)

The output would therefore be something like:

Race start: 43, 60, 22
End of lap 1: 43, 60, 22
End of lap 2: 43, 60, 22

(It doesn't need to be formatted like that. Standard code golf input / output rules apply).

Some things to consider:

  • a car may crash, and therefore will stop appearing in the order after some number of laps
  • a car may be lapped. In this case, it may also not complete the full number of laps, as the race may end once the leader has completed their final lap
  • an overtake may occur during a lap

Test Cases

[10,20,30,10,20,30,10,20,30]
[
 [10,20,30],
 [10,20,30],
 [10,20,30]
]
[10,20,30,20,10,30,20,10]
[
 [10,20,30],
 [20,10,30],
 [20,10]
]
[10,20,30,10,20,10,30,20,30]
[
 [10,20,30],
 [10,20,30],
 [10,20,30]
]
[10,20,30,10,20,30,10,20,10,30,20,10,20,30]
[
 [10,20,30],
 [10,20,30],
 [10,20,30],
 [10,20,30],
 [10,20]
]
[10]
[
 [10]
]
[30,20,40,10,20,20,20,20,20,30,10,30,40]
[
 [30,20,40,10],
 [20,30,10,40],
 [20,30],
 [20],
 [20],
 [20]
]

Other notes / rules

  • inputs will always contain at least 1 number (i.e.: a single car starting a 1 lap race, and crashing before finishing)
  • code golf scoring
  • all car numbers will be integers greater than or equal to 1
\$\endgroup\$
8
  • 1
    \$\begingroup\$ How do we know when a car gets lapped? \$\endgroup\$
    – Xcali
    Commented Jul 15 at 16:24
  • 2
    \$\begingroup\$ If the race ends when the leader crosses the last time, shouldn't your first output be [10,20,30],[10,20,30],[10]? (And all the others likewise modified?) \$\endgroup\$
    – nitsua60
    Commented Jul 15 at 17:11
  • \$\begingroup\$ @Xcali when another driver has a lap difference >1 \$\endgroup\$ Commented Jul 15 at 19:29
  • 1
    \$\begingroup\$ @nitsua60 I think the race may end when the leader finishes (which is effectively the same as the others all then crashing), rather than it will end when the leader finishes. \$\endgroup\$ Commented Jul 15 at 22:09
  • 1
    \$\begingroup\$ @Shaggy I would say no, but you can have some imaginary internet points for a solution which saves a few bytes using that (in addition to the main solution which formats it properly) ✨✨ \$\endgroup\$
    – Ted
    Commented Jul 17 at 4:47

12 Answers 12

8
\$\begingroup\$

JavaScript, 68 bytes

a=>a.map(c=>o.find(r=>!r.includes(c))?.push(c)??o.push([c]),o=[])&&o

f=
a=>a.map(c=>o.find(r=>!r.includes(c))?.push(c)??o.push([c]),o=[])&&o
console.log(JSON.stringify(f([10,20,30,10,20,30,10,20,30])))
console.log(JSON.stringify(f([10,20,30,20,10,30,20,10])))
console.log(JSON.stringify(f([10,20,30,10,20,10,30,20,30])))
console.log(JSON.stringify(f([10,20,30,10,20,30,10,20,10,30,20,10,20,30])))

\$\endgroup\$
8
\$\begingroup\$

JavaScript (ES11), 54 bytes

a=>a.map(p=n=>(o[p[n]=p[n]+1|0]||=[]).push(n),o=[])&&o

Attempt This Online!

\$\endgroup\$
4
  • \$\begingroup\$ Cool usage of a recursive function assignment (I think?) within a map! Hard to wrap my head around haha \$\endgroup\$
    – Ted
    Commented Jul 15 at 12:38
  • 1
    \$\begingroup\$ @Ted There's no recursion here. p=n=> is just a way to get an object p which is used to count the occurrences of drivers. \$\endgroup\$
    – Arnauld
    Commented Jul 15 at 12:54
  • 1
    \$\begingroup\$ Right... I think I understand. So p is an object (which also happens to be a function) and you're assigning values to keys of p as well. \$\endgroup\$
    – Ted
    Commented Jul 15 at 13:06
  • \$\begingroup\$ @Ted Yes, exactly. For the same byte count, we could also re-use the input array with negative indices. \$\endgroup\$
    – Arnauld
    Commented Jul 15 at 17:34
6
\$\begingroup\$

Jelly, 5 bytes

ĠZṢ€ị

A monadic Link that accepts a list of car numbers in lap marker observed race order and yields a list of lists of car numbers at progressive laps, each in race order.

Try it online!

How?

ĠZṢ€ị - Link: List, Cars  e.g. [7, 1, 3, 7, 3, 7, 1, 3, 3]
Ġ     - group the indices of {Cars} by their respective values
                            -> [[2, 7], [3, 5, 8, 9], [1, 4, 6]]
 Z    - transpose           -> [[2, 3, 1], [7, 5, 4], [8, 6], [9]]
  Ṣ€  - sort each           -> [[1, 2, 3], [4, 5, 7], [6, 8], [9]]
    ị - index into {Cars}   -> [[7, 1, 3], [7, 3, 1], [7, 3], [3]]
\$\endgroup\$
4
\$\begingroup\$

Charcoal, 17 bytes

IE⌈Eθ№θιΦθ⁼ι№…θμλ

Try it online! Link is to verbose version of code. Explanation:

    θ               Input array
   E                Map over elements
     №              Count of
       ι            Current element
      θ             In input array
  ⌈                 Take the maximum
 E                  Map over implicit range
         θ          Input array
        Φ           Filtered where
            №       Count of
                λ   Inner element in
              θ     Input array
             …      Truncated to length
               μ    Inner index
          ⁼         Equals
           ι        Outer value
I                   Cast to string
                    Implicitly print
\$\endgroup\$
4
\$\begingroup\$

05AB1E, 17 11 7 bytes

.¡IN£s¢

-6 bytes porting JonathanAllan's Jelly answer (but without convenient Ġ builtin).
-4 bytes thanks to @Neil

Try it online or verify all test cases.

Original 17 bytes answer:

vÐÙþkDŠõsǝ}\¯)Ù¨è

Try it online or verify all test cases.

Explanation:

.¡       # Group the values in the (implicit) input-list by:
  I      #  Push the input-list
   N     #  Push the 0-based group_by-index
    £    #  Leave that many leading items from the input-list
     s   #  Swap so the current value is at the top of the stack
      ¢  #  Count how many times it occurs in this input-list prefix
         # (after which the list of lists of values is output implicitly)
v        # Loop the (implicit) input-size amount of times:
 Ð       #  Triplicate the current list
         #  (which will be the implicit input-list in the first iteration)
  Ù      #  Uniquify the top copy
   þ     #  Remove potential empty strings by only keeping numbers
    k    #  Get all (0-based) indices of these values in the duplicated list
 D       #  Duplicate that list of indices
  Š      #  Triple-swap to make the order indices,list,indices
   õsǝ   #  Insert empty strings at those indices in the list,
         #  to mark the current lap as done
}\       # After the loop: discard the top list of empty strings
  ¯      # Push an empty list (work-around for single-lap input-lists)
   )     # Wrap all other lists of indices into a list
    Ù¨   # Uniquify and remove the last item to remove all trailing empty lists
      è  # Index each inner index into the (implicit) input-list
         # (after which the list of lists of values is output implicitly)
\$\endgroup\$
6
  • 1
    \$\begingroup\$ There is a problem with IÙk. Because of it cars are always listed in order they started the race instead of the order they finished a loop (e.g. the 2nd loop of the 2nd test test gives [10,20,30] instead of [20,10,30]). vÐʒ.ï}Ùkª¤õsǝ}ʒgĀ}è would work properly (we unify integer elements, i.e. car numbers, of the current list instead of initial; and hence we don't need to remove -1s later). \$\endgroup\$
    – Mat_rdv
    Commented Jul 15 at 20:35
  • 1
    \$\begingroup\$ @JonathanAllan Thanks for noticing. Fixed my original approach while golfing 2 bytes, and also golfed an additional -5 bytes porting your answer (but without convenient Ġ builtin). \$\endgroup\$ Commented Jul 15 at 21:45
  • 1
    \$\begingroup\$ @Mat_rdv Thanks. When I saw Jonathan's answer, I indeed concluded the same regarding the . Thanks for the potential fix, but I've fixed it slightly different while golfing 2 bytes at the same time (after which I golfed 5 more bytes porting Jonathan's Jelly approach). \$\endgroup\$ Commented Jul 15 at 21:46
  • 1
    \$\begingroup\$ I wish .γ¹N£y¢ worked; Σ¹N£y¢}©.γ®N£y¢ is far too long. \$\endgroup\$
    – Neil
    Commented Jul 16 at 5:34
  • \$\begingroup\$ @Neil Thanks for -4 bytes! The builtin you were looking for is . (And yes, I agree it's not too obvious from the Wiki Commands page.. I had the same problem a few years ago.) \$\endgroup\$ Commented Jul 16 at 6:52
4
\$\begingroup\$

Python3, 86 bytes

def f(c):
 while c:
  l=[[],[]]
  for i in c:l[i not in l[1]]+=[i]
  yield l[1];c=l[0]

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Uiua, 4 bytes

⊕□⊸⧆

Try it!

⊕ group into □ boxes ⊸ by ⧆ occurences.
⧆ occurences gives the number of times each element of an array has appeared (up to the current index), so ⧆[1 2 1 3 1 2] is [0 0 1 0 2 1].

\$\endgroup\$
4
\$\begingroup\$

Japt, 8 bytes

I tried this initially and it didn't work; Nyxbird's solution inspired me to try it again and it worked this time 🤷🏼‍♂️

ü@¯Y è¶X

Try it

ü@¯Y è¶X     :Implicit input of array U
ü            :Group by
 @           :Passing each X at index Y through the following function
  ¯Y         :  Slice U to index Y
     è       :  Count the elements
      ¶X     :    Equal to X

Original, 10 bytes

Was stuck on 12 for ages, with the possibility of 10 only dawning on me just before I posted. If outputting a 3D-array were allowed then the last 3 bytes could be replaced with o.

í üÎÕËñÌmÎ

Try it

í üÎÕËñÌmÎ     :Implicit input of array
í              :Interleave with indices
  ü            :Group & sort by
   Î           :  First element (car)
    Õ          :Transpose
     Ë         :Map
      ñ        :  Sort by
       Ì       :    Last element
        m      :  Map to
         Î     :    First elements
\$\endgroup\$
2
\$\begingroup\$

Clojure, 121 bytes

#(map reverse(vals(second(reduce(fn[[R P]c](let[r(+(or(R c)0)1)][(assoc R c r)(update P r conj c)]))[{}(sorted-map)]%))))

TIO

\$\endgroup\$
2
\$\begingroup\$

C64 Basic, 135 bytes

C64 code

The code has 135 bytes without counting the DATA lines which may be different depending on the test case.

Additional limitations:

  • car numbers can only be integer values up to 255
  • the total number of cars cannot exceed 28

C64 screenshot

Try in emulator

\$\endgroup\$
2
\$\begingroup\$

Haskell + hgl, 13 bytes

uef nb$df*^nb

Attempt This Online!

Explanation

  • nb gets all the distinct elements of a list (basically which cars are still in the race)
  • df*^nb subtracts the result of nb from the total list. i.e. it removes one lap.
  • uef iterates the above two functions until the list is empty and collects the results of the nbs.

21 bytes

uue$l2m(rv<<nx<rv)nbr

Attempt This Online!

Explanation

Two functions uue and nbr almost solve this task. uue is an "unfold", basically a variant of uef from the first solution. It takes a function which breaks a list apart and repeats it until the input is empty collecting the pieces.

nbr takes the first occurrence of every element of the list out, however it reorders the remaining elements. So instead of doing what we want uue nbr just sort of repeats the first lap a couple of times, eliminating cars if they don't finish a lap.

So we have to fix this ordering problem, and we end up paying quite a bit to do this. What I do is take the intersection of the remainder with the reverse of the original list. This takes the order of the reversed original list and imposes it on the fragment. Then I reverse the result.

This is done with this monstrosity: rv<<nx<rv

Reflection

I think this is a really good challenge. I'm happy enough with the first answer, but I'm pretty unhappy with the second. hgl is really close to being really short with just uue nbr, but it fumbles it. I'm even disappointed in how many bytes it takes to fix the issue in the second answer.

So I have a couple of things I think could be improved:

  • I sort of understand why nbr is the way it is. But I ultimately think it is a poor choice. Implementing it the way that this challenge needs would be useful, and probably would be a faster function. But instead of changing the behavior, since it's a 3 byte built in, I think I should just add an alternate version.
  • If I'm doing that I might as well add df*^nb as a function. It wouldn't save bytes here, but might as well.
  • I spend too much on rv<<nx<rv. This is extremely close to the "sandwich" operation swc. There should be a version of the sandwich operation for binary functions, something like sw2. This would be more analogous rv<<(nx.*rv) but both work for shorter. It would only save bytes if sw2 was given an infix as well.
\$\endgroup\$
0
\$\begingroup\$

Nim, 112 109 107 bytes

proc(s= @[1]):auto=(var l:seq[s.type];for c in s:(var i=0;while(l.grow(i+1,@[]);c in l[i]):i+=1
l[i]&=c)
l)

Try it in Wandbox! (with added let f= header)

Ungolfed:

proc raceOrder(cars = @[1]): auto =
  var laps: seq[cars.type]
  for car in cars:
    var i=0
    while(laps.grow(i+1,@[]); car in laps[i]):
      i+=1
    laps[i] &= car
  return laps
\$\endgroup\$
2
  • \$\begingroup\$ Would incrementing i after assigning the current car to the correct subarray allow you to get rid of the laps.del part? \$\endgroup\$
    – Shaggy
    Commented Jul 19 at 16:20
  • \$\begingroup\$ @Shaggy I don't think so? var l= @[@[1]] would have an extra element @[1] that needs to be removed at some point. But thank you for comment, I looked at the code one more time and noticed that implicit init is shorter. \$\endgroup\$
    – janAkali
    Commented Jul 20 at 4:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.