logo Netzschleuder network catalog, repository, and centrifuge

History of fics_games

The changes to the source code of this entry in the repository, newest first, each with the differences it made to the entry's files. They are also listed on GitLab.

[entries] Add the date when each entry was added, and remove icon_hash by Tiago de Paula Peixoto beff7c9
The dates are those of the commits that first added the entries' files
(2020-08-11 for the entries of the initial commit); renamed entries keep
the date of their first name.

Changed: none
diff --git a/entries/fics_games/__init__.py b/entries/fics_games/__init__.py
index d7755d9..ee6dbdc 100644
--- a/entries/fics_games/__init__.py
+++ b/entries/fics_games/__init__.py
@@ -31,7 +31,7 @@ url = ['https://dynamics.cs.washington.edu/data.html',
        'https://dynamics.cs.washington.edu/nobackup/chess']
 citation = [('M. Fire and C. Guestrin, "The rise and fall of network stars: Analyzing 2.5 million graphs to reveal how high-degree vertices emerge over time." Information Processing & Management 57(2), 102041 (2020)',
              'https://doi.org/10.1016/j.ipm.2019.05.002')]
-icon_hash = None
+date_added = '2026-10-31'
 upstream_license = None
 
 upstream_prefix = 'https://dynamics.cs.washington.edu/nobackup/chess'
[entries] Prefix the properties in the descriptions with (Node), (Edge) or (Graph) by Tiago de Paula Peixoto bc3dbbd
The lists of properties at the end of the descriptions marked edge
properties with "Edge" but left node properties unmarked, or grouped
them in "Nodes: `a`, ...; `b`, ..." items, so the kind of a property
was often unclear. Every item now starts with (Node), (Edge) or (Graph),
followed by the property names, a colon and the explanation, one item
per property or group of properties; qualifications (kinds of nodes,
networks) follow the names in parentheses. Properties that were only
named got a short explanation, and a few items that named the wrong
kind of property were corrected.
diff --git a/entries/fics_games/__init__.py b/entries/fics_games/__init__.py
index 0220d25..d7755d9 100644
--- a/entries/fics_games/__init__.py
+++ b/entries/fics_games/__init__.py
@@ -23,9 +23,9 @@ description = r"""A network of the games of chess played on the Free Internet Ch
 
 The network has the following properties:
 
-- `name`: the account name of the player.
-- `first_game`, `last_game`: the times of the first and last games of the player, in Unix time.
-- Edge `time`: the time of the game, in Unix time (as given in the data, at the precision of minutes)."""
+- (Node) `name`: the account name of the player.
+- (Node) `first_game`, `last_game`: the times of the first and last games of the player, in Unix time.
+- (Edge) `time`: the time of the game, in Unix time (as given in the data, at the precision of minutes)."""
 tags = ['Social', 'Online', 'Sports', 'Multigraph', 'Timestamps', 'Temporal', 'Metadata']
 url = ['https://dynamics.cs.washington.edu/data.html',
        'https://dynamics.cs.washington.edu/nobackup/chess']
[entries] New entry: fics_games by Tiago de Paula Peixoto 46d6749
diff --git a/entries/fics_games/__init__.py b/entries/fics_games/__init__.py
new file mode 100644
index 0000000..0220d25
--- /dev/null
+++ b/entries/fics_games/__init__.py
@@ -0,0 +1,101 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2026 Tiago de Paula Peixoto <tiago@skewed.de>
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Affero General Public License along
+# with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from .. import *
+
+title = "Free Internet Chess Server games (1999-2016)"
+description = r"""A network of the games of chess played on the Free Internet Chess Server (FICS) from January 1999 to the end of 2015, as extracted by M. Fire and C. Guestrin from the FICS Games Database for their study of the evolution of network stars. Each node is a player (an account of the server), and an edge connects two players for each game they played (a multigraph), with the time of the game. The data give each game twice, once in each direction, without the colors of the players; each game is included once, and the network is undirected (games of a player against itself, if any, are left out). The results, the ratings, the time controls and the moves of the games are not part of the data.
+
+The network has the following properties:
+
+- `name`: the account name of the player.
+- `first_game`, `last_game`: the times of the first and last games of the player, in Unix time.
+- Edge `time`: the time of the game, in Unix time (as given in the data, at the precision of minutes)."""
+tags = ['Social', 'Online', 'Sports', 'Multigraph', 'Timestamps', 'Temporal', 'Metadata']
+url = ['https://dynamics.cs.washington.edu/data.html',
+       'https://dynamics.cs.washington.edu/nobackup/chess']
+citation = [('M. Fire and C. Guestrin, "The rise and fall of network stars: Analyzing 2.5 million graphs to reveal how high-degree vertices emerge over time." Information Processing & Management 57(2), 102041 (2020)',
+             'https://doi.org/10.1016/j.ipm.2019.05.002')]
+icon_hash = None
+upstream_license = None
+
+upstream_prefix = 'https://dynamics.cs.washington.edu/nobackup/chess'
+files = [(("fcis.tar.gz:./FCIS/fcis_chess.vertices.csv",
+           "fcis.tar.gz:./FCIS/fcis_chess.interactions.csv"), None, None)]
+
+def fetch_upstream(force=False):
+    return fetch_upstream_files(__name__.split(".")[-1], upstream_prefix, files,
+                                force)
+
+# For checks only: if set, only (about) this many rows of the games are read
+_check_rows = None
+
+def _read(f, cols, types):
+    import pyarrow
+    import pyarrow.csv
+    return pyarrow.csv.open_csv(
+        f, read_options=pyarrow.csv.ReadOptions(block_size=1 << 26,
+                                                use_threads=False),
+        convert_options=pyarrow.csv.ConvertOptions(
+            column_types=dict(zip(cols, types)),
+            timestamp_parsers=["%Y%m%dT%H%M%S"]))
+
+@cache_network()
+@coerce_props()
+@annotate()
+def parse(alts=None):
+    import pyarrow
+    import pyarrow.compute as pc
+    name = __name__.split(".")[-1]
+    ts = pyarrow.timestamp("s")
+    for fnames, alt, fmt in files:
+        if alts is not None and alt not in alts:
+            continue
+        with open_upstream_file(name, fnames[0], "rb") as f:
+            vs = _read(f, ["mindate", "v_id", "maxdate"],
+                       [ts, pyarrow.string(), ts]).read_all()
+        names = vs.column("v_id").combine_chunks()
+        g = Graph(directed=False)
+        g.add_vertex(len(names))
+        g.vp.name = g.new_vp("string", vals=names.to_pylist())
+        g.vp.first_game = g.new_vp("int64_t")
+        g.vp.last_game = g.new_vp("int64_t")
+        g.vp.first_game.a = pc.cast(vs.column("mindate"), pyarrow.int64()).to_numpy()
+        g.vp.last_game.a = pc.cast(vs.column("maxdate"), pyarrow.int64()).to_numpy()
+        del vs
+        g.ep.time = g.new_ep("int64_t")
+        n = 0
+        with open_upstream_file(name, fnames[1], "rb") as f:
+            for b in _read(f, ["datetime", "src_id", "dst_id"],
+                           [ts, pyarrow.string(), pyarrow.string()]):
+                s = pc.index_in(b.column("src_id"), value_set=names).to_numpy(zero_copy_only=False)
+                t = pc.index_in(b.column("dst_id"), value_set=names).to_numpy(zero_copy_only=False)
+                tm = pc.cast(b.column("datetime"), pyarrow.int64()).to_numpy()
+                # (the players are all in the list of vertices)
+                assert not (numpy.isnan(s.astype(float)).any() or
+                            numpy.isnan(t.astype(float)).any())
+                s = s.astype("int64")
+                t = t.astype("int64")
+                # each game is given in both directions: one is kept
+                sel = s < t
+                g.add_edge_list(numpy.column_stack([s[sel], t[sel], tm[sel]]),
+                                eprops=[g.ep.time])
+                n += b.num_rows
+                if _check_rows is not None and n >= _check_rows:
+                    break
+        yield alt, g