g | x | w | all
Bytes Lang Time Link
014Japt !250720T132950ZShaggy
048Janet250720T104337ZAdam
024Uiua241127T044539ZErikDaPa
014Retina250221T153816Zmbomb007
022Bash241126T075733Zroblogic
032Google Sheets241127T143917Zdoubleun
024Zsh241126T025025Zroblogic
020Perl 5 p241127T035434ZXcali
018sed rn241126T231546ZJan Blum
046Python 3241126T215341Zxnor
019Charcoal241126T111133ZNeil
044APL+WIN241126T105806ZGraham
059Python 3241126T101629ZRhaixer
01605AB1E241126T084607ZKevin Cr
026JavaScript V8241126T014956Zl4m2
035JavaScript V8241126T013402Zthejonym
019Vyxal241126T013210Zlyxal

Japt -!, 14 bytes

i è"cie|[^c]ei

Try it

Janet, 48 bytes

|(peg/match~(+"ei"(to"cie")(to(*(!"c")1"ei")))$)                                                                                        

Returns nil for valid inputs and @[] for invalid inputs.

Uiua, 20 24 bytes (thx to Jan Blumschein's correction)

¬±⧻regex"cie|[^c]ei|^ei"

Explanation:

¬±⧻regex"cie|[^c]ei|^ei"
   regex"cie|[^c]ei|^ei" => find matches of illegal combos
  ⧻                      => is there a match?
±                       => signum as a bool value and negate

Try this online!

Retina, 14 bytes

I'm surprised nobody used Retina. It outgolfs all the existing answers!

([^c]|^)ei|cie

Returns 0 for truthy, or a positive number for falsy.

I like that the start of the regex kind of looks like an emote-esque face. I will also note that I came up with this answer and regex prior to viewing existing answers.

Run online


If you want to view nice TRUE and FALSE output, use this code in the online interpreter:

C`([^c]|^)ei|cie
0
TRUE
\d+
FALSE

Bash, 25 22 bytes

egrep "[^c]ei|cie|^ei"

Try it online!   25 bytes

Returns 1 for truthy (input obeys the rule), 0 for falsy.

Google Sheets, 32 bytes

=1-regexmatch(1&A1,"cie|[^c]ei")

Uses the same regex as many others. Gets 0 for false and 1 for true. Remove the 1- bit to get true for false and false for true and save two bytes.

screenshot

Zsh, 26 24 bytes

-2 bytes, thanks to @GammaFunction

${1:/(*cie|(|*[^c])ei)*}

Try it Online!   26 bytes

Returns 127 for truthy (input obeys the rule), 0 for falsey.

Perl 5 -p, 20 bytes

$_=!/cie|(^|[^c])ei/

Try it online!

sed -rn, 20 18 bytes

/^ei|[^c]ei|cie/q1

Takes one string on stdin, and returns exit code 0 (1) for strings that do (do not) follow the rule.

Try it online!

Python 3, 46 bytes

lambda s:(q:=s.count)("cie")+q("ei")<=q("cei")

Try it online!

Checks that the count of cie's is zero, and that the the count of ei's equal that of cei's. These are combined into one inequality as cie's + ei's <= cei's. If we may output true/false flipped, we can negate the >= to < to save 1.

50 bytes

lambda s:not re.search("cie|(?<!c)ei",s)
import re

Try it online!

A regex solution. The outer not converts match objects to False, and Nones indicating no match to False. If we could do truthy/falsy reversed, we could use re.compile for 43 bytes:

import re
re.compile("cie|(?<!c)ei").search

Try it online!

Charcoal, 19 bytes

¬∨№θcie⁻⌕Aθei⁺¹⌕Aθc

Try it online! Link is to verbose version of code. Outputs a Charcoal boolean, i.e. - for ie and cei only, nothing if ei or cie. Explanation:

  №                 Count of
    cie             Literal string `cie`
   θ                In input string
 ∨                  Logical Or
        ⌕A          Find All
           ei       Literal string `ei`
          θ         In input string
       ⁻            Set difference
               ⌕A   Find All
                  c Literal string `c`
                 θ  In input sring
             ⁺      Vectorised Plus
              ¹     Literal integer `1`
¬                   Logical Not
                    Implicitly print

Note that using the newer version of Charcoal on ATO I could have saved a byte by using Incremented() instead of Plus(1, ).

APL+WIN, 44 bytes

Prompts for string. Outputs 1=true, 0=false

3=+/2 4 5≠(4⍴2)⊥+/¨ 'cei' 'cie' 'ei' 'ie'⍷¨⊂⎕

Try it online! Thanks to Dyalog Classic

Python 3, 59 bytes

Just another boring Python solution with re. None is falsy, an re.Match object is truthy. Basically, the regex confirms the string has no cies, and then any eis without a c behind. It matches the string if both conditions succeeded.

lambda n:re.match("^(?!.*cie)(?!.*(?<!c)ei).*",n)
import re

Try it online!

If you're confused about the True output, expand the header and footer in case it doesn't show. The footer contains some testing code to assert that the function does indeed work according to the test cases.

05AB1E, 20 16 bytes

¬ìAε„eiN<iR}«}åà

Outputs an inverted boolean (0 for truthy; 1 for falsey).

Try it online or verify all test cases.

Explanation:

¬         # Get the first character of the (implicit) input-string
 ì        # Prepend this letter to the input-string
  A       # Push the alphabet
   ε      # Map over each letter:
    „ei   #  Push string "ei"
    N<i   #  If the 0-based index is 2:
       R  #   Reverse it to "ie"
      }«  #  After the if-statement, append it to the letter
   }å     # After the map: check for each whether it's a substring in the input
     à    # Pop and check if any is truthy
          # (after which the result is output implicitly)

The “ is for edge-cases that start with ei (or test case "ei" itself).

JavaScript (V8), 26 bytes

x=>!/cie|[^c]ei/.test(0+x)

Try it online!

Same length x=>!/cie|(?<!c)ei/.test(x)

JavaScript (V8), 35 bytes

x=>/^((?!(?<!c)ei|cie).)*$/.test(x)

Try it online!

Admittedly identical to the regex solution ATaco found in chat but we came to our conclusions independently. And mine is in funny javascript.

x=>/^((?!(?<!c)ei|cie).)*$/.test(x)  //full program
x=>/                      /.test(x)  //js regex boilerplate
    ^                    $           //within the entire string
     (                .)*            //for every character,
                                     //(if there are any)
      (?!            )               //so long as the next few
                                     //characters are not
                 |                     //either
                  cie                    //"cie"
               ei                        //or an "ei"
         (?<!c)                          //that isnt after "c"
                                     //return true, qed

Vyxal, 152 bitsv2, 19 bytes

`cie`c¬?`.?ei`Ẏ`cei`=A∧

Try it Online!

Bitstring:

01101101101000001011111100110011000001010100101101100011101000011110111001011111100111000011101110101010000001000101100101110100001001011010101010010011

The footer converts the 0/1 to FALSE/TRUE respectively for the test cases.

Returns true if cie isn't present and all instances of ei are preceded by a c.

Explained

`cie`c¬?`.?ei`Ẏ`cei`=A∧­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏⁠‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏⁠‎⁡⁠⁢⁣‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁢⁢⁣‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁢⁢‏‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏⁠‎⁡⁠⁢⁡⁢‏⁠‎⁡⁠⁢⁡⁣‏⁠‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏‏​⁡⁠⁡‌­
`cie`c¬                  # ‎⁡Is "cie" absent in the input
                      ∧  # ‎⁢And
                     A   # ‎⁣Are all
        `.?ei`Ẏ          # ‎⁤Instances of `ei` with the (optional) letter that comes beforehand
               `cei`=    # ‎⁢⁡Equal to cei? 
💎

Created with the help of Luminespire.