home · contact · privacy
Some renamings to improve interface logic, terseness.
authorPlom Heller <plom@plomlompom.com>
Sun, 31 May 2026 23:06:24 +0000 (01:06 +0200)
committerPlom Heller <plom@plomlompom.com>
Sun, 31 May 2026 23:06:24 +0000 (01:06 +0200)
bricksplom.py

index 8aed7e6aaeeb097de736833567bc3048e319f578..b4f69aa10f0272b773eec9418f5f041789f4bb6d 100755 (executable)
@@ -7,10 +7,10 @@ from pathlib import Path
 from typing import Callable, Optional, Self
 
 NAME_ENV_DIRNAME = 'BRICKSPLOM_DIR'
-PATH_COLLECTIONS = 'collections.txt'
+PATH_SETS = 'sets.txt'
 PATH_COLORS = 'colors.txt'
 PATH_DESIGNS = 'designs.txt'
-PATH_PIECES = 'pieces.txt'
+PATH_BRICKS = 'bricks.txt'
 
 CHAR_NEWLINE = '\n'
 CHAR_SEP_TOKEN = ' '
@@ -30,8 +30,8 @@ CHAR_Q_STUDS = 's'
 CHAR_Q_TEXT = '?'
 BOX_PREFIX = 'box:'
 
-PieceListing = tuple[int, str, str]
-PageColumn = tuple[PieceListing, ...]
+BrickListing = tuple[int, str, str]
+PageColumn = tuple[BrickListing, ...]
 Page = tuple[PageColumn, ...]
 
 
@@ -141,7 +141,7 @@ class Lookupable:
         return self.matchers[query_char](query_body)
 
 
-class Color(Textfiled, Lookupable):
+class BrickColor(Textfiled, Lookupable):
     'Color incl. solidness/transparency field.'
     _id_indent = 3
 
@@ -177,7 +177,7 @@ class Color(Textfiled, Lookupable):
                 + self.wavelength)
 
 
-class Design(Textfiled, Lookupable):
+class BrickDesign(Textfiled, Lookupable):
     'Shape and texture configurations with descriptions and equalities.'
     _id_indent = 6
     alternate_to: Optional[Self] = None
@@ -263,7 +263,7 @@ class Design(Textfiled, Lookupable):
                          + f' {self._description}')))
 
 
-class Piece(Textfiled, WithDb, Lookupable):
+class Brick(Textfiled, WithDb, Lookupable):
     'Individual configuration of design and color.'
     _id_indent = 7
 
@@ -290,19 +290,21 @@ class Piece(Textfiled, WithDb, Lookupable):
             ) -> dict[str, Self]:
         collected = {}
         for toks in [cls.tokify(line, 3) for line in cls.lines_of(path)]:
-            piece_id, design_id = toks[:2]
-            assert piece_id not in collected
+            brick_id, design_id = toks[:2]
+            assert brick_id not in collected
             color_id, comment = (toks[-1].split(CHAR_SEP_TOKEN, maxsplit=1)
                                  + [''])[:2]
-            collected[piece_id] = cls(piece_id, design_id, color_id, comment,
+            collected[brick_id] = cls(brick_id, design_id, color_id, comment,
                                       db=db)
         return collected
 
     def raw(
             self
             ) -> str:
-        return (f'{self.id_indented()} {Design.indent_id(self.design_id)} '
-                f'{Color.indent_id(self.color_id)} {self.comment}').rstrip()
+        return (f'{self.id_indented()} '
+                f'{BrickDesign.indent_id(self.design_id)} '
+                f'{BrickColor.indent_id(self.color_id)} {self.comment}'
+                ).rstrip()
 
     def __str__(
             self
@@ -311,25 +313,26 @@ class Piece(Textfiled, WithDb, Lookupable):
         design = self._db.designs[self.design_id]
         color = str(self._db.colors[self.color_id]).strip()
         comment = f' # {self.comment}' if self.comment else ''
-        return (f'{self.id_indented()} {Design.indent_id(self.design_id)} '
+        return (f'{self.id_indented()} '
+                f'{BrickDesign.indent_id(self.design_id)} '
                 f'{design.description} ({color}){comment}')
 
 
-class Collection(Textfiled, WithDb, Lookupable):
-    'Named collection of pieces in order of pages of columns of counts.'
+class BrickSet(Textfiled, WithDb, Lookupable):
+    'Named collection of bricks in order of pages of columns of counts.'
 
     def __init__(
             self,
             id_: str,
             is_in: Optional[bool],
             description: str,
-            piece_listings: tuple[Page, ...],
+            brick_listings: tuple[Page, ...],
             **kwargs
             ) -> None:
         self.id_ = id_
         self.is_in = is_in
         self.description = description
-        self.piece_listings = piece_listings
+        self.brick_listings = brick_listings
         super().__init__(**kwargs)
 
     @classmethod
@@ -341,9 +344,9 @@ class Collection(Textfiled, WithDb, Lookupable):
             ) -> dict[str, Self]:
         collected: dict[str, tuple[Optional[bool],
                                    str,
-                                   list[list[list[PieceListing]]]]]
+                                   list[list[list[BrickListing]]]]]
         collected = {}
-        i_listings: list[list[list[PieceListing]]] = [[[]]]
+        i_listings: list[list[list[BrickListing]]] = [[[]]]
         for line in cls.lines_of(path):
             if not line.startswith(CHAR_COLL_INDENT):
                 id_, metadata = cls.tokify(line, 2)
@@ -370,7 +373,7 @@ class Collection(Textfiled, WithDb, Lookupable):
             k: cls(id_=k,
                    is_in=v[0],
                    description=v[1],
-                   piece_listings=tuple(tuple(tuple(column) for column in page)
+                   brick_listings=tuple(tuple(tuple(column) for column in page)
                                         for page in v[2]),
                    db=db)
             for k, v in collected.items()}
@@ -388,7 +391,7 @@ class Collection(Textfiled, WithDb, Lookupable):
         return (f'{self.id_indented()} '
                 f'{self._is_in_str}{self.description}{CHAR_NEWLINE}'
                 + self._format_paginated(lambda count, p_id, comment:
-                                         f' {count:2} {Piece.indent_id(p_id)}'
+                                         f' {count:2} {Brick.indent_id(p_id)}'
                                          + (f' {comment}' if comment else '')))
 
     def __str__(
@@ -409,20 +412,20 @@ class Collection(Textfiled, WithDb, Lookupable):
             format_line: Callable[[int, str, str], str]
             ) -> str:
         lines = []
-        for idx_pages, page in enumerate(self.piece_listings):
+        for idx_pages, page in enumerate(self.brick_listings):
             if idx_pages != 0:
                 lines += [' =']
             for idx_columns, column in enumerate(page):
                 if idx_columns != 0:
                     lines += [' -']
-                for count, piece_id, comment in column:
-                    lines += [format_line(count, piece_id, comment)]
+                for count, brick_id, comment in column:
+                    lines += [format_line(count, brick_id, comment)]
         return CHAR_NEWLINE.join(lines) + CHAR_NEWLINE
 
-    def piece_listings_flat(self) -> tuple[PieceListing, ...]:
-        'Flattened variant of .piece_listings, no division into pages/cols.'
-        collected: list[PieceListing] = []
-        for page in self.piece_listings:
+    def brick_listings_flat(self) -> tuple[BrickListing, ...]:
+        'Flattened variant of .brick_listings, no division into pages/cols.'
+        collected: list[BrickListing] = []
+        for page in self.brick_listings:
             for column in page:
                 collected += list(column)
         return tuple(collected)
@@ -430,19 +433,19 @@ class Collection(Textfiled, WithDb, Lookupable):
     def show(
             self
             ) -> str:
-        def format_line(count, piece_id, comment) -> str:
+        def format_line(count, brick_id, comment) -> str:
             assert self._db
-            piece = self._db.pieces[piece_id]
-            design_id = piece.design_id
+            brick = self._db.bricks[brick_id]
+            design_id = brick.design_id
             tail_comment = f' # {comment}' if comment else ''
-            color = str(self._db.colors[self._db.pieces[piece_id].color_id]
+            color = str(self._db.colors[self._db.bricks[brick_id].color_id]
                         ).lstrip().split(CHAR_SEP_TOKEN, maxsplit=1)[1]
             box: Optional[Box] = None
             for i_box in self._db.boxes().values():
                 for idx_in_box in [
                         idx for idx, d_to_ls
                         in enumerate(i_box.designs_to_listings)
-                        if piece_id in [listing[1] for listing in d_to_ls[1]]]:
+                        if brick_id in [listing[1] for listing in d_to_ls[1]]]:
                     box = i_box
                     break
                 if box:
@@ -459,9 +462,10 @@ class Collection(Textfiled, WithDb, Lookupable):
                         break
             box_listing = ((box.id_indented() if box else Box.indent_id(''))
                            + ':' + (f'{idx_in_box:>2}' if box else '__'))
-            return (f'{count:>2}× {Piece.indent_id(piece_id)}:'
-                    f'{Design.indent_id(design_id)} {box_listing} {color} '
-                    f'{self._db.designs[design_id].description}{tail_comment}')
+            return (
+                f'{count:>2}× {Brick.indent_id(brick_id)}:'
+                f'{BrickDesign.indent_id(design_id)} {box_listing} {color} '
+                f'{self._db.designs[design_id].description}{tail_comment}')
 
         return f'{self}{CHAR_NEWLINE}{self._format_paginated(format_line)}'
 
@@ -473,16 +477,16 @@ class Box(WithDb, Lookupable):
     def __init__(
             self,
             id_: str,
-            collection: Collection,
+            bricks_set: BrickSet,
             **kwargs
             ) -> None:
         super().__init__(**kwargs)
         assert self._db
         self.id_ = id_
-        self._collection = collection
-        designed_listings: list[tuple[Design, list[PieceListing]]] = []
-        for listing in self._collection.piece_listings_flat():
-            design = self._db.designs[self._db.pieces[listing[1]].design_id]
+        self._set = bricks_set
+        designed_listings: list[tuple[BrickDesign, list[BrickListing]]] = []
+        for listing in self._set.brick_listings_flat():
+            design = self._db.designs[self._db.bricks[listing[1]].design_id]
             design = design.alternate_to or design
             if not (designed_listings and designed_listings[-1][0] == design):
                 designed_listings += [(design, []), ]
@@ -506,44 +510,43 @@ class Box(WithDb, Lookupable):
         for design, listings in self.designs_to_listings:
             lines += [
                 f'=== {" / ".join(design.all_ids)}: {design.description} ===']
-            for count, piece_id, comment in listings:
-                color = self._db.colors[self._db.pieces[piece_id].color_id]
-                lines += [f'{count:>2}× {Piece.indent_id(piece_id)} / {color} '
+            for count, brick_id, comment in listings:
+                color = self._db.colors[self._db.bricks[brick_id].color_id]
+                lines += [f'{count:>2}× {Brick.indent_id(brick_id)} / {color} '
                           f'# {comment}']
         return CHAR_NEWLINE.join(lines)
 
     def raw(
             self
             ) -> str:
-        'Call .raw() of base Collection.'
-        return self._collection.raw()
+        'Call .raw() of base BrickSet.'
+        return self._set.raw()
 
 
 class BricksDb:
     'Collection of all the tables enabling their combined processing.'
     lookupable = frozenset(
-            {'boxes', 'designs', 'collections', 'colors', 'pieces'})
+            {'boxes', 'designs', 'sets', 'colors', 'bricks'})
 
     def __init__(
             self,
             path_tables: str
             ) -> None:
-        self.colors = Color.from_textfile((path_tables, PATH_COLORS))
-        self.pieces = Piece.from_textfile((path_tables, PATH_PIECES), db=self)
-        self.collections = Collection.from_textfile(
-                (path_tables, PATH_COLLECTIONS), db=self)
-        self.designs = Design.from_textfile((path_tables, PATH_DESIGNS))
+        self.colors = BrickColor.from_textfile((path_tables, PATH_COLORS))
+        self.bricks = Brick.from_textfile((path_tables, PATH_BRICKS), db=self)
+        self.sets = BrickSet.from_textfile((path_tables, PATH_SETS), db=self)
+        self.designs = BrickDesign.from_textfile((path_tables, PATH_DESIGNS))
         self._check_consistencies_between_tables()
 
     def boxes(
             self
             ) -> dict[str, Box]:
-        'Parsed from BOX_PREFIX-prefixed entries in .collections.'
+        'Parsed from BOX_PREFIX-prefixed entries in .sets.'
         collected = {}
-        for collection in [c for c in self.collections.values()
+        for bricks_set in [c for c in self.sets.values()
                            if c.id_.startswith(BOX_PREFIX)]:
-            box_id = collection.id_[len(BOX_PREFIX):]
-            collected[box_id] = Box(box_id, collection, db=self)
+            box_id = bricks_set.id_[len(BOX_PREFIX):]
+            collected[box_id] = Box(box_id, bricks_set, db=self)
         return collected
 
     def _check_consistencies_between_tables(
@@ -551,38 +554,38 @@ class BricksDb:
             ) -> None:
         fails = []
 
-        # check all items listed in collections recorded in pieces
-        for coll in self.collections.values():
-            for piece_id in [t[1] for t in coll.piece_listings_flat()
-                             if t[1] not in self.pieces]:
-                fails += [f'missing Piece of ID: {piece_id}']
+        # check all items listed in sets recorded in bricks
+        for coll in self.sets.values():
+            for brick_id in [t[1] for t in coll.brick_listings_flat()
+                             if t[1] not in self.bricks]:
+                fails += [f'missing brick of ID: {brick_id}']
 
-        # check all pieces' designs recorded in designs
-        for d_id in [p.design_id for p in self.pieces.values()
+        # check all bricks' designs recorded in designs
+        for d_id in [p.design_id for p in self.bricks.values()
                      if p.design_id not in self.designs]:
-            fails += [f'missing Design of ID: {d_id}']
+            fails += [f'missing design of ID: {d_id}']
 
-        # check all recorded designs have matching pieces (at least via alts)
+        # check all recorded designs have matching bricks (at least via alts)
         for design_id in self.designs:
-            if not [p for p in self.pieces.values()
+            if not [p for p in self.bricks.values()
                     if p.design_id == design_id]:
-                fails += [f'missing Pieces for design of ID: {design_id}']
+                fails += [f'missing bricks for design of ID: {design_id}']
 
-        # check all pieces' colors are recorded
-        for color_id in [v.color_id for v in self.pieces.values()
+        # check all bricks' colors are recorded
+        for color_id in [v.color_id for v in self.bricks.values()
                          if v.color_id not in self.colors]:
-            fails += [f'missing Color of ID: {color_id}']
+            fails += [f'missing color of ID: {color_id}']
 
-        # check collections in-out directions even out
+        # check sets' in-out directions even out
         counts: dict[str, int] = {}
-        for coll in [coll for coll in self.collections.values()
+        for coll in [coll for coll in self.sets.values()
                      if coll.is_in is not None]:
-            for count, piece_id, _ in coll.piece_listings_flat():
-                counts[piece_id] = (counts.get(piece_id, 0)
+            for count, brick_id, _ in coll.brick_listings_flat():
+                counts[brick_id] = (counts.get(brick_id, 0)
                                     + ((1 if coll.is_in else (-1)) * count))
-        for piece_id, count in [(k, v) for k, v in counts.items()
+        for brick_id, count in [(k, v) for k, v in counts.items()
                                 if v != 0]:
-            fails += [f'invalid count for Piece of ID: {piece_id} ({count})']
+            fails += [f'invalid count for brick of ID: {brick_id} ({count})']
 
         # print, and crash on, collected fails, if any
         for fail in fails: