0% found this document useful (0 votes)
2 views

Practical Artificial Intelligence Programming With Java 3rd Mark Watson instant download

The document is a promotional and informational piece about the book 'Practical Artificial Intelligence Programming With Java' by Mark Watson, which is available for download. It includes links to various related eBooks on artificial intelligence programming in different languages and topics. The book covers a wide range of AI concepts, including search algorithms, reasoning, expert systems, and machine learning, among others.

Uploaded by

leurcalbbas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Practical Artificial Intelligence Programming With Java 3rd Mark Watson instant download

The document is a promotional and informational piece about the book 'Practical Artificial Intelligence Programming With Java' by Mark Watson, which is available for download. It includes links to various related eBooks on artificial intelligence programming in different languages and topics. The book covers a wide range of AI concepts, including search algorithms, reasoning, expert systems, and machine learning, among others.

Uploaded by

leurcalbbas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 90

Practical Artificial Intelligence Programming

With Java 3rd Mark Watson download

https://ebookbell.com/product/practical-artificial-intelligence-
programming-with-java-3rd-mark-watson-2540348

Explore and download more ebooks at ebookbell.com


Here are some recommended products that we believe you will be
interested in. You can click the link to download.

Practical Artificial Intelligence Programming With Clojure Mark Watson

https://ebookbell.com/product/practical-artificial-intelligence-
programming-with-clojure-mark-watson-46856614

Python Machine Learning Learn Python In A Week And Master It An


Handson Introduction To Artificial Intelligence Coding A Projectbased
Guide With Practical Exercises Book 2 Academy

https://ebookbell.com/product/python-machine-learning-learn-python-in-
a-week-and-master-it-an-handson-introduction-to-artificial-
intelligence-coding-a-projectbased-guide-with-practical-exercises-
book-2-academy-11359930

Practical Artificial Intelligence For Internet Of Medical Things Ben


Othman Soufene

https://ebookbell.com/product/practical-artificial-intelligence-for-
internet-of-medical-things-ben-othman-soufene-48959886

Practical Artificial Intelligence Machine Learning Bots And Agent


Solutions Using C 1st Ed Arnaldo Prez Castao

https://ebookbell.com/product/practical-artificial-intelligence-
machine-learning-bots-and-agent-solutions-using-c-1st-ed-arnaldo-prez-
castao-55536140
Practical Artificial Intelligence For Dummies Kris Hammond

https://ebookbell.com/product/practical-artificial-intelligence-for-
dummies-kris-hammond-22120870

Practical Artificial Intelligence And Blockchain 1st Edition Ganesh


Prasad Kumble

https://ebookbell.com/product/practical-artificial-intelligence-and-
blockchain-1st-edition-ganesh-prasad-kumble-23866162

Practical Artificial Intelligence Machine Learning Bots And Agent


Solutions Using C Arnaldoprezcastao

https://ebookbell.com/product/practical-artificial-intelligence-
machine-learning-bots-and-agent-solutions-using-c-
arnaldoprezcastao-7025548

Practical Artificial Intelligence An Enterprise Playbook Alan


Pelzsharpe Kashyap Kompella

https://ebookbell.com/product/practical-artificial-intelligence-an-
enterprise-playbook-alan-pelzsharpe-kashyap-kompella-48861318

Practical Artificial Intelligence With Swift From Fundamental Theory


To Development Of Aidriven Apps Paperback Mars Geldard

https://ebookbell.com/product/practical-artificial-intelligence-with-
swift-from-fundamental-theory-to-development-of-aidriven-apps-
paperback-mars-geldard-10553784
Practical Artificial Intelligence
Programming With Java

Third Edition

Mark Watson
Copyright 2001-2008 Mark Watson. All rights reserved.
This work is licensed under a Creative Commons
Attribution-Noncommercial-No Derivative Works
Version 3.0 United States License.

November 11, 2008


Contents

Preface xi

1 Introduction 1
1.1 Other JVM Languages . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Why is a PDF Version of this Book Available Free on the Web? . . . 1
1.3 Book Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Use of Java Generics and Native Types . . . . . . . . . . . . . . . . 2
1.5 Notes on Java Coding Styles Used in this Book . . . . . . . . . . . 3
1.6 Book Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2 Search 5
2.1 Representation of Search State Space and Search Operators . . . . . 5
2.2 Finding Paths in Mazes . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Finding Paths in Graphs . . . . . . . . . . . . . . . . . . . . . . . . 13
2.4 Adding Heuristics to Breadth First Search . . . . . . . . . . . . . . 22
2.5 Search and Game Playing . . . . . . . . . . . . . . . . . . . . . . . 22
2.5.1 Alpha-Beta Search . . . . . . . . . . . . . . . . . . . . . . 22
2.5.2 A Java Framework for Search and Game Playing . . . . . . 24
2.5.3 Tic-Tac-Toe Using the Alpha-Beta Search Algorithm . . . . 29
2.5.4 Chess Using the Alpha-Beta Search Algorithm . . . . . . . 34

3 Reasoning 45
3.1 Logic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.1.1 History of Logic . . . . . . . . . . . . . . . . . . . . . . . 47
3.1.2 Examples of Different Logic Types . . . . . . . . . . . . . 47
3.2 PowerLoom Overview . . . . . . . . . . . . . . . . . . . . . . . . 48
3.3 Running PowerLoom Interactively . . . . . . . . . . . . . . . . . . 49
3.4 Using the PowerLoom APIs in Java Programs . . . . . . . . . . . . 52
3.5 Suggestions for Further Study . . . . . . . . . . . . . . . . . . . . 54

4 Semantic Web 57
4.1 Relational Database Model Has Problems Dealing with Rapidly Chang-
ing Data Requirements . . . . . . . . . . . . . . . . . . . . . . . . 58
4.2 RDF: The Universal Data Format . . . . . . . . . . . . . . . . . . . 59
4.3 Extending RDF with RDF Schema . . . . . . . . . . . . . . . . . . 62
4.4 The SPARQL Query Language . . . . . . . . . . . . . . . . . . . . 63
4.5 Using Sesame . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

iii
Contents

4.6 OWL: The Web Ontology Language . . . . . . . . . . . . . . . . . 69


4.7 Knowledge Representation and REST . . . . . . . . . . . . . . . . 71
4.8 Material for Further Study . . . . . . . . . . . . . . . . . . . . . . 72

5 Expert Systems 73
5.1 Production Systems . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2 The Drools Rules Language . . . . . . . . . . . . . . . . . . . . . 75
5.3 Using Drools in Java Applications . . . . . . . . . . . . . . . . . . 77
5.4 Example Drools Expert System: Blocks World . . . . . . . . . . . 81
5.4.1 POJO Object Models for Blocks World Example . . . . . . 82
5.4.2 Drools Rules for Blocks World Example . . . . . . . . . . . 85
5.4.3 Java Code for Blocks World Example . . . . . . . . . . . . 88
5.5 Example Drools Expert System: Help Desk System . . . . . . . . . 90
5.5.1 Object Models for an Example Help Desk . . . . . . . . . . 91
5.5.2 Drools Rules for an Example Help Desk . . . . . . . . . . . 93
5.5.3 Java Code for an Example Help Desk . . . . . . . . . . . . 95
5.6 Notes on the Craft of Building Expert Systems . . . . . . . . . . . . 97

6 Genetic Algorithms 99
6.1 Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2 Java Library for Genetic Algorithms . . . . . . . . . . . . . . . . . 101
6.3 Finding the Maximum Value of a Function . . . . . . . . . . . . . . 105

7 Neural Networks 109


7.1 Hopfield Neural Networks . . . . . . . . . . . . . . . . . . . . . . 110
7.2 Java Classes for Hopfield Neural Networks . . . . . . . . . . . . . 111
7.3 Testing the Hopfield Neural Network Class . . . . . . . . . . . . . 114
7.4 Back Propagation Neural Networks . . . . . . . . . . . . . . . . . 116
7.5 A Java Class Library for Back Propagation . . . . . . . . . . . . . . 119
7.6 Adding Momentum to Speed Up Back-Prop Training . . . . . . . . 127

8 Machine Learning with Weka 129


8.1 Using Weka’s Interactive GUI Application . . . . . . . . . . . . . . 130
8.2 Interactive Command Line Use of Weka . . . . . . . . . . . . . . . 132
8.3 Embedding Weka in a Java Application . . . . . . . . . . . . . . . 134
8.4 Suggestions for Further Study . . . . . . . . . . . . . . . . . . . . 136

9 Statistical Natural Language Processing 137


9.1 Tokenizing, Stemming, and Part of Speech Tagging Text . . . . . . 137
9.2 Named Entity Extraction From Text . . . . . . . . . . . . . . . . . 141
9.3 Using the WordNet Linguistic Database . . . . . . . . . . . . . . . 144
9.3.1 Tutorial on WordNet . . . . . . . . . . . . . . . . . . . . . 144
9.3.2 Example Use of the JAWS WordNet Library . . . . . . . . 145
9.3.3 Suggested Project: Using a Part of Speech Tagger to Use
the Correct WordNet Synonyms . . . . . . . . . . . . . . . 149

iv
Contents

9.3.4 Suggested Project: Using WordNet Synonyms to Improve


Document Clustering . . . . . . . . . . . . . . . . . . . . . 150
9.4 Automatically Assigning Tags to Text . . . . . . . . . . . . . . . . 150
9.5 Text Clustering . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
9.6 Spelling Correction . . . . . . . . . . . . . . . . . . . . . . . . . . 156
9.6.1 GNU ASpell Library and Jazzy . . . . . . . . . . . . . . . 157
9.6.2 Peter Norvig’s Spelling Algorithm . . . . . . . . . . . . . . 158
9.6.3 Extending the Norvig Algorithm by Using Word Pair Statistics162
9.7 Hidden Markov Models . . . . . . . . . . . . . . . . . . . . . . . . 166
9.7.1 Training Hidden Markov Models . . . . . . . . . . . . . . . 168
9.7.2 Using the Trained Markov Model to Tag Text . . . . . . . . 173

10 Information Gathering 177


10.1 Open Calais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
10.2 Information Discovery in Relational Databases . . . . . . . . . . . 181
10.2.1 Creating a Test Derby Database Using the CIA World Fact-
Book and Data on US States . . . . . . . . . . . . . . . . . 182
10.2.2 Using the JDBC Meta Data APIs . . . . . . . . . . . . . . . 183
10.2.3 Using the Meta Data APIs to Discern Entity Relationships . 187
10.3 Down to the Bare Metal: In-Memory Index and Search . . . . . . . 187
10.4 Indexing and Search Using Embedded Lucene . . . . . . . . . . . . 193
10.5 Indexing and Search with Nutch Clients . . . . . . . . . . . . . . . 197
10.5.1 Nutch Server Fast Start Setup . . . . . . . . . . . . . . . . 198
10.5.2 Using the Nutch OpenSearch Web APIs . . . . . . . . . . . 201

11 Conclusions 207

v
Contents

vi
List of Figures

2.1 A directed graph representation is shown on the left and a two-


dimensional grid (or maze) representation is shown on the right. In
both representations, the letter R is used to represent the current po-
sition (or reference point) and the arrowheads indicate legal moves
generated by a search operator. In the maze representation, the two
grid cells marked with an X indicate that a search operator cannot
generate this grid location. . . . . . . . . . . . . . . . . . . . . . . 7
2.2 UML class diagram for the maze search Java classes . . . . . . . . 8
2.3 Using depth first search to find a path in a maze finds a non-optimal
solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Using breadth first search in a maze to find an optimal solution . . . 14
2.5 UML class diagram for the graph search classes . . . . . . . . . . . 15
2.6 Using depth first search in a sample graph . . . . . . . . . . . . . . 21
2.7 Using breadth first search in a sample graph . . . . . . . . . . . . . 21
2.8 Alpha-beta algorithm applied to part of a game of tic-tac-toe . . . . 23
2.9 UML class diagrams for game search engine and tic-tac-toe . . . . . 30
2.10 UML class diagrams for game search engine and chess . . . . . . . 35
2.11 The example chess program does not contain an opening book so it
plays to maximize the mobility of its pieces and maximize material
advantage using a two-move lookahead. The first version of the
chess program contains a few heuristics like wanting to control the
center four squares. . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.12 Continuing the first sample game: the computer is looking ahead
two moves and no opening book is used. . . . . . . . . . . . . . . . 37
2.13 Second game with a 2 1/2 move lookahead. . . . . . . . . . . . . . 41
2.14 Continuing the second game with a two and a half move lookahead.
We will add more heuristics to the static evaluation method to reduce
the value of moving the queen early in the game. . . . . . . . . . . 42

3.1 Overview of how we will use PowerLoom for development and de-
ployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

4.1 Layers of data models used in implementing Semantic Web applica-


tions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.2 Java utility classes and interface for using Sesame . . . . . . . . . . 68

vii
List of Figures

5.1 Using Drools for developing rule-based systems and then deploying
them. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2 Initial state of a blocks world problem with three blocks stacked on
top of each other. The goal is to move the blocks so that block C is
on top of block A. . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.3 Block C has been removed from block B and placed on the table. . . 82
5.4 Block B has been removed from block A and placed on the table. . . 84
5.5 The goal is solved by placing block C on top of block A. . . . . . . 85

6.1 The test function evaluated over the interval [0.0, 10.0]. The maxi-
mum value of 0.56 occurs at x=3.8 . . . . . . . . . . . . . . . . . . 100
6.2 Crossover operation . . . . . . . . . . . . . . . . . . . . . . . . . . 101

7.1 Physical structure of a neuron . . . . . . . . . . . . . . . . . . . . . 110


7.2 Two views of the same two-layer neural network; the view on the
right shows the connection weights between the input and output
layers as a two-dimensional array. . . . . . . . . . . . . . . . . . . 117
7.3 Sigmoid and derivative of the Sigmoid (SigmoidP) functions. This
plot was produced by the file src-neural-networks/Graph.java. . . . 118
7.4 Capabilities of zero, one, and two hidden neuron layer neural net-
works. The grayed areas depict one of two possible output values
based on two input neuron activation values. Note that this is a
two-dimensional case for visualization purposes; if a network had
ten input neurons instead of two, then these plots would have to be
ten-dimensional instead of two-dimensional. . . . . . . . . . . . . . 119
7.5 Example backpropagation neural network with one hidden layer. . . 120
7.6 Example backpropagation neural network with two hidden layers. . 120

8.1 Running the Weka Data Explorer . . . . . . . . . . . . . . . . . . . 131


8.2 Running the Weka Data Explorer . . . . . . . . . . . . . . . . . . . 131

viii
List of Tables

2.1 Runtimes by Method for Chess Program . . . . . . . . . . . . . . . 44

6.1 Random chromosomes and the floating point numbers that they encode106

9.1 Most commonly used part of speech tags . . . . . . . . . . . . . . . 139


9.2 Sample part of speech tags . . . . . . . . . . . . . . . . . . . . . . 167
9.3 Transition counts from the first tag (shown in row) to the second tag
(shown in column). We see that the transition from NNP to VB is
common. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
9.4 Normalize data in Table 9.3 to get probability of one tag (seen in
row) transitioning to another tag (seen in column) . . . . . . . . . . 171
9.5 Probabilities of words having specific tags. Only a few tags are
shown in this table. . . . . . . . . . . . . . . . . . . . . . . . . . . 172

ix
List of Tables

x
Preface
I wrote this book for both professional programmers and home hobbyists who al-
ready know how to program in Java and who want to learn practical Artificial In-
telligence (AI) programming and information processing techniques. I have tried to
make this an enjoyable book to work through. In the style of a “cook book,” the
chapters can be studied in any order. Each chapter follows the same pattern: a mo-
tivation for learning a technique, some theory for the technique, and a Java example
program that you can experiment with.

I have been interested in AI since reading Bertram Raphael’s excellent book Think-
ing Computer: Mind Inside Matter in the early 1980s. I have also had the good
fortune to work on many interesting AI projects including the development of com-
mercial expert system tools for the Xerox LISP machines and the Apple Macintosh,
development of commercial neural network tools, application of natural language
and expert systems technology, medical information systems, application of AI tech-
nologies to Nintendo and PC video games, and the application of AI technologies to
the financial markets.

I enjoy AI programming, and hopefully this enthusiasm will also infect the reader.

Software Licenses for example programs in this book

My example programs for chapters using Open Source Libraries are released under
the same licenses as the libraries:

• Drools Expert System Demos: Apache style license

• PowerLoom Reasoning: LGPL

• Sesame Semantic Web: LGPL

The licenses for the rest of my example programs are in the directory licenses-for-
book-code:

• License for commercial use: if you purchase a print version of this book or
the for-fee PDF version from Lulu.com then you can use any of my code and
data used in the book examples under a non-restrictive license. This book can
be purchaed at http://www.lulu.com/content/4502573

• Free for non-commercial and academic use: if you use the free PDF version

xi
Preface

of this book you can use the code and data used in the book examples free for
activities that do not generate revenue.

Acknowledgements

I would like to thank Kevin Knight for writing a flexible framework for game search
algorithms in Common LISP (Rich, Knight 1991) and for giving me permission to
reuse his framework, rewritten in Java for some of the examples in Chapter 2. I have
a library full of books on AI and I would like to thank the authors of all of these
books for their influence on my professional life. I frequently reference books in the
text that have been especially useful to me and that I recommend to my readers.

In particular, I would like to thank the authors of the following two books that have
had the most influence on me:

• Stuart Russell and Peter Norvig’s Artificial Intelligence: A Modern Approach


which I consider to be the best single reference book for AI theory

• John Sowa’s book Knowledge Representation is a resource that I frequently


turn to for a holistic treatment of logic, philosophy, and knowledge represen-
tation in general

Book Editor:

Carol Watson

Thanks to the following people who found typos:

Carol Watson, James Fysh, Joshua Cranmer, Jack Marsh, Jeremy Burt, Jean-Marc
Vanel

xii
1 Introduction

There are many fine books on Artificial Intelligence (AI) and good tutorials and
software on the web. This book is intended for professional programmers who either
already have an interest in AI or need to use specific AI technologies at work.

The material is not intended as a complete reference for AI theory. Instead, I provide
enough theoretical background to understand the example programs and to provide
a launching point if you want or need to delve deeper into any of the topics covered.

1.1 Other JVM Languages

The Java language and JVM platform are very widely used so that techniques that
you learn can be broadly useful. There are other JVM languages like JRuby, Clojure,
Jython, and Scala that can use existing Java classes. While the examples in this book
are written in Java you should have little trouble using my Java example classes and
the open source libraries with these alternative JVM languages.

1.2 Why is a PDF Version of this Book Available


Free on the Web?

I have written 14 books that have been published by the traditional publishers Springer-
Verlag, McGraw-Hill, J. Wiley, Morgan Kaufman, Hungry Minds, MCP, and Sybex.
This is my first book that I have produced and published on my own and my moti-
vation for this change is the ability to write for smaller niche markets on topics that
most interest me.

As an author I want to both earn a living writing and have many people read and
enjoy my books. By offering for sale both a print version and a for-fee PDF version
for purchase at http://www.lulu.com/content/4502573 I can earn some money for
my efforts and also allow readers who can not afford to buy many books or may
only be interested in a few chapters of this book to read the free PDF version that is
available from my web site.

1
1 Introduction

Please note that I do not give permission to post the free PDF version of this book on
other people’s web sites: I consider this to be commercial exploitation in violation
of the Creative Commons License that I have chosen for this book. Having my free
web books only available on my web site brings viewers to my site and helps attract
customers for my consulting business. I do encourage you to copy the PDF for this
book onto your own computer for local reading and it is fine to email copies of the
free PDF to friends.

If you enjoy reading the no-cost PDF version of this book I would also appreciate it
if you would purchase a print copy using the purchase link:

http://www.lulu.com/content/4502573

I thank you for your support.

1.3 Book Software

You can download a large ZIP file containing all code and test data used in this book
from the URL:

http://markwatson.com/opencontent/javaai_3rd_code.zip

All the example code that I have written is covered by the licenses discussed in the
Preface.

The code examples usually consist of reusable (non GUI) libraries and throwaway
text-based test programs to solve a specific application problem; in some cases, the
test code will contain a test or demonstration GUI.

1.4 Use of Java Generics and Native Types

In general I usually use Java generics and the new collection classes for almost
all of my Java programming. That is also the case for the examples in this book
except when using native types and arrays provides a real performance advantage
(for example, in the search examples).

Since arrays must contain reifiable types they play poorly with generics so I prefer
not to mix coding styles in the same code base. There are some obvious cases where
not using primitive types leads to excessive object creation and boxing/unboxing.
That said, I expect Java compilers, Hotspot, and the JVM in general to keep getting
better and this may be a non-issue in the future.

2
1.5 Notes on Java Coding Styles Used in this Book

1.5 Notes on Java Coding Styles Used in this


Book

Many of the example programs do not strictly follow common Java programming
idioms – this is usually done for brevity. For example, when a short example is all
in one Java package I will save lines of code and programing listing space by not
declaring class data private with public getters and setters; instead, I will sometimes
simply use package visibility as in this example:

public static class Problem {


// constants for appliance types:
enum Appliance {REFRIGERATOR, MICROWAVE, TV, DVD};
// constants for problem types:
enum ProblemType {NOT_RUNNING, SMOKING, ON_FIRE,
MAKES_NOISE};
// constants for environmental data:
enum EnvironmentalDescription {CIRCUIT_BREAKER_OFF,
LIGHTS_OFF_IN_ROOM};
Appliance applianceType;
List<ProblemType> problemTypes =
new ArrayList<ProblemType>();
List<EnvironmentalDescription> environmentalData =
new ArrayList<EnvironmentalDescription>();
// etc.
}

Please understand that I do not advocate this style of programming in large projects
but one challenge in writing about software development is the requirement to make
the examples short and easily read and understood. Many of the examples started as
large code bases for my own projects that I “whittled down” to a small size to show
one or two specific techniques. Forgoing the use of “getters and setters” in many of
the examples is just another way to shorten the examples.

Authors of programming books are faced with a problem in formatting program


snippets: limited page width. You will frequently see what would be a single line in
a Java source file split over two or three lines to accommodate limited page width as
seen in this example:

private static void


createTestFacts(WorkingMemory workingMemory)
throws Exception {
...
}

3
1 Introduction

1.6 Book Summary

Chapter 1 is the introduction for this book.

Chapter 2 deals with heuristic search in two domains: two-dimensional grids (for
example mazes) and graphs (defined by nodes and edges connecting nodes).

Chapter 3 covers logic, knowledge representation, and reasoning using the Power-
Loom system.

Chapter 4 covers the Semantic Web. You will learn how to use RDF and RDFS
data for knowledge representation and how to use the popular Sesame open source
Semantic Web system.

Chapter 5 introduces you to rule-based or production systems. We will use the


open source Drools system to implement simple expert systems for solving “blocks
world” problems and to simulate a help desk system.

Chapter 6 gives an overview of Genetic Algorithms, provides a Java library, and


solves a test problem. The chapter ends with suggestions for projects you might
want to try.

Chapter 7 introduces Hopfield and Back Propagation Neural Networks. In addition


to Java libraries you can use in your own projects, we will use two Swing-based Java
applications to visualize how neural networks are trained.

Chapter 8 introduces you to the GPLed Weka project. Weka is a best of breed toolkit
for solving a wide range of machine learning problems.

Chapter 9 covers several Statistical Natural Language Processing (NLP) techniques


that I often use in my own work: processing text (tokenizing, stemming, and de-
termining part of speech), named entity extraction from text, using the WordNet
lexical database, automatically assigning tags to text, text clustering, three different
approaches to spelling correction, and a short tutorial on Markov Models.

Chapter 10 provides useful techniques for gathering and using information: using
the Open Calais web services for extracting semantic information from text, infor-
mation discovery in relational databases, and three different approaches to indexing
and searching text.

4
2 Search

Early AI research emphasized the optimization of search algorithms. This approach


made a lot of sense because many AI tasks can be solved effectively by defining
state spaces and using search algorithms to define and explore search trees in this
state space. Search programs were frequently made tractable by using heuristics to
limit areas of search in these search trees. This use of heuristics converts intractable
problems to solvable problems by compromising the quality of solutions; this trade
off of less computational complexity for less than optimal solutions has become a
standard design pattern for AI programming. We will see in this chapter that we
trade off memory for faster computation time and better results; often, by storing
extra data we can make search time faster, and make future searches in the same
search space even more efficient.

What are the limitations of search? Early on, search applied to problems like check-
ers and chess misled early researchers into underestimating the extreme difficulty of
writing software that performs tasks in domains that require general world knowl-
edge or deal with complex and changing environments. These types of problems
usually require the understanding and then the implementation of domain specific
knowledge.

In this chapter, we will use three search problem domains for studying search algo-
rithms: path finding in a maze, path finding in a graph, and alpha-beta search in the
games tic-tac-toe and chess.

2.1 Representation of Search State Space and


Search Operators

We will use a single search tree representation in graph search and maze search
examples in this chapter. Search trees consist of nodes that define locations in state
space and links to other nodes. For some small problems, the search tree can be
easily specified statically; for example, when performing search in game mazes, we
can compute and save a search tree for the entire state space of the maze. For many
problems, it is impossible to completely enumerate a search tree for a state space
so we must define successor node search operators that for a given node produce
all nodes that can be reached from the current node in one step; for example, in the

5
2 Search

game of chess we can not possibly enumerate the search tree for all possible games
of chess, so we define a successor node search operator that given a board position
(represented by a node in the search tree) calculates all possible moves for either
the white or black pieces. The possible chess moves are calculated by a successor
node search operator and are represented by newly calculated nodes that are linked
to the previous node. Note that even when it is simple to fully enumerate a search
tree, as in the game maze example, we still might want to generate the search tree
dynamically as we will do in this chapter).

For calculating a search tree we use a graph. We will represent graphs as node with
links between some of the nodes. For solving puzzles and for game related search,
we will represent positions in the search space with Java objects called nodes. Nodes
contain arrays of references to both child and parent nodes. A search space using
this node representation can be viewed as a directed graph or a tree. The node that
has no parent nodes is the root node and all nodes that have no child nodes a called
leaf nodes.

Search operators are used to move from one point in the search space to another.
We deal with quantized search spaces in this chapter, but search spaces can also be
continuous in some applications. Often search spaces are either very large or are
infinite. In these cases, we implicitly define a search space using some algorithm
for extending the space from our reference position in the space. Figure 2.1 shows
representations of search space as both connected nodes in a graph and as a two-
dimensional grid with arrows indicating possible movement from a reference point
denoted by R.

When we specify a search space as a two-dimensional array, search operators will


move the point of reference in the search space from a specific grid location to
an adjoining grid location. For some applications, search operators are limited to
moving up/down/left/right and in other applications operators can additionally move
the reference location diagonally.

When we specify a search space using node representation, search operators can
move the reference point down to any child node or up to the parent node. For
search spaces that are represented implicitly, search operators are also responsible
for determining legal child nodes, if any, from the reference point.

Note that I use different libraries for the maze and graph search examples.

2.2 Finding Paths in Mazes

The example program used in this section is MazeSearch.java in the directory sr-
c/search/maze and I assume that the reader has downloaded the entire example ZIP
file for this book and placed the source files for the examples in a convenient place.

6
2.2 Finding Paths in Mazes

R
R

Figure 2.1: A directed graph representation is shown on the left and a two-
dimensional grid (or maze) representation is shown on the right. In
both representations, the letter R is used to represent the current posi-
tion (or reference point) and the arrowheads indicate legal moves gener-
ated by a search operator. In the maze representation, the two grid cells
marked with an X indicate that a search operator cannot generate this
grid location.

Figure 2.2 shows the UML class diagram for the maze search classes: depth first
and breadth first search. The abstract base class AbstractSearchEngine contains
common code and data that is required by both the classes DepthF irstSearch
and BreadthF irstSearch. The class M aze is used to record the data for a two-
dimensional maze, including which grid locations contain walls or obstacles. The
class M aze defines three static short integer values used to indicate obstacles, the
starting location, and the ending location.

The Java class M aze defines the search space. This class allocates a two-dimensional
array of short integers to represent the state of any grid location in the maze. When-
ever we need to store a pair of integers, we will use an instance of the standard Java
class java.awt.Dimension, which has two integer data components: width and
height. Whenever we need to store an x-y grid location, we create a new Dimension
object (if required), and store the x coordinate in Dimension.width and the y coor-
dinate in Dimension.height. As in the right-hand side of Figure 2.1, the operator
for moving through the search space from given x-y coordinates allows a transition
to any adjacent grid location that is empty. The Maze class also contains the x-y
location for the starting location (startLoc) and goal location (goalLoc). Note that
for these examples, the class Maze sets the starting location to grid coordinates 0-0
(upper left corner of the maze in the figures to follow) and the goal node in (width -
1)-(height - 1) (lower right corner in the following figures).

7
2 Search

AbstractSearchEngine
Maze
AbstractSearchEngine
Maze
getPath: Dimension []
#searchPath 1 getValue: short
setValue: void
#initSearch

DepthFirstSearchEngine
BreadthFirstSearchEngine
DepthFirstSearchEngine
BreadthFirstSearchEngine
iterateSearch
1
1

MazeDepthFirstSearch MazeBreadthFirstSearch
MazeDepthFirstSearch MazeBreadthFirstSearch
paint paint
main (static) main (static)

Java main test


programs using JFC

Figure 2.2: UML class diagram for the maze search Java classes

The abstract class AbstractSearchEngine is the base class for both the depth
first (uses a stack to store moves) search class DepthF irstSearchEngine and the
breadth first (uses a queue to store moves) search class BreadthF irstSearchEngine.
We will start by looking at the common data and behavior defined in AbstractSearchEngine.
The class constructor has two required arguments: the width and height of the maze,
measured in grid cells. The constructor defines an instance of the M aze class of
the desired size and then calls the utility method initSearch to allocate an array
searchP ath of Dimension objects, which will be used to record the path traversed
through the maze. The abstract base class also defines other utility methods:

• equals(Dimensiond1, Dimensiond2) – checks to see if two arguments of


type Dimension are the same.

• getP ossibleM oves(Dimensionlocation) – returns an array of Dimension


objects that can be moved to from the specified location. This implements the
movement operator.

Now, we will look at the depth first search procedure. The constructor for the derived
class DepthF irstSearchEngine calls the base class constructor and then solves
the search problem by calling the method iterateSearch. We will look at this
method in some detail. The arguments to iterateSearch specify the current location
and the current search depth:

8
2.2 Finding Paths in Mazes

private void iterateSearch(Dimension loc, int depth)

The class variable isSearching is used to halt search, avoiding more solutions, once
one path to the goal is found.

if (isSearching == false) return;

We set the maze value to the depth for display purposes only:

maze.setValue(loc.width, loc.height, (short)depth);

Here, we use the super class getP ossibleM oves method to get an array of possible
neighboring squares that we could move to; we then loop over the four possible
moves (a null value in the array indicates an illegal move):

Dimension [] moves = getPossibleMoves(loc);


for (int i=0; i<4; i++) {
if (moves[i] == null) break; // out of possible moves
// from this location

Record the next move in the search path array and check to see if we are done:

searchPath[depth] = moves[i];
if (equals(moves[i], goalLoc)) {
System.out.println("Found the goal at " +
moves[i].width +
‘‘, " + moves[i].height);
isSearching = false;
maxDepth = depth;
return;
} else {

If the next possible move is not the goal move, we recursively call the iterateSearch
method again, but starting from this new location and increasing the depth counter
by one:

iterateSearch(moves[i], depth + 1);


if (isSearching == false) return;
}

9
2 Search

Figure 2.3: Using depth first search to find a path in a maze finds a non-optimal
solution

Figure 2.3 shows how poor a path a depth first search can find between the start and
goal locations in the maze. The maze is a 10-by-10 grid. The letter S marks the
starting location in the upper left corner and the goal position is marked with a G
in the lower right corner of the grid. Blocked grid cells are painted light gray. The
basic problem with the depth first search is that the search engine will often start
searching in a bad direction, but still find a path eventually, even given a poor start.
The advantage of a depth first search over a breadth first search is that the depth first
search requires much less memory. We will see that possible moves for depth first
search are stored on a stack (last in, first out data structure) and possible moves for
a breadth first search are stored in a queue (first in, first out data structure).

The derived class BreadthF irstSearch is similar to the DepthF irstSearch pro-
cedure with one major difference: from a specified search location we calculate
all possible moves, and make one possible trial move at a time. We use a queue
data structure for storing possible moves, placing possible moves on the back of the
queue as they are calculated, and pulling test moves from the front of the queue. The

10
2.2 Finding Paths in Mazes

effect of a breadth first search is that it “fans out” uniformly from the starting node
until the goal node is found.

The class constructor for BreadthF irstSearch calls the super class constructor to
initialize the maze, and then uses the auxiliary method doSearchOn2Dgrid for per-
forming a breadth first search for the goal. We will look at the class BreadthF irstSearch
in some detail. Breadth first search uses a queue instead of a stack (depth first search)
to store possible moves. The utility class DimensionQueue implements a standard
queue data structure that handles instances of the class Dimension.

The method doSearchOn2Dgrid is not recursive, it uses a loop to add new search
positions to the end of an instance of class DimensionQueue and to remove and
test new locations from the front of the queue. The two-dimensional array allReadyV isited
keeps us from searching the same location twice. To calculate the shortest path after
the goal is found, we use the predecessor array:

private void doSearchOn2DGrid() {


int width = maze.getWidth();
int height = maze.getHeight();
boolean alReadyVisitedFlag[][] =
new boolean[width][height];
Dimension predecessor[][] =
new Dimension[width][height];
DimensionQueue queue =
new DimensionQueue();
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
alReadyVisitedFlag[i][j] = false;
predecessor[i][j] = null;
}
}

We start the search by setting the already visited flag for the starting location to true
value and adding the starting location to the back of the queue:

alReadyVisitedFlag[startLoc.width][startLoc.height]
= true;
queue.addToBackOfQueue(startLoc);
boolean success = false;

This outer loop runs until either the queue is empty or the goal is found:

outer:
while (queue.isEmpty() == false) {

11
2 Search

We peek at the Dimension object at the front of the queue (but do not remove it)
and get the adjacent locations to the current position in the maze:

Dimension head = queue.peekAtFrontOfQueue();


Dimension [] connected =
getPossibleMoves(head);

We loop over each possible move; if the possible move is valid (i.e., not null) and
if we have not already visited the possible move location, then we add the possible
move to the back of the queue and set the predecessor array for the new location to
the last square visited (head is the value from the front of the queue). If we find the
goal, break out of the loop:

for (int i=0; i<4; i++) {


if (connected[i] == null) break;
int w = connected[i].width;
int h = connected[i].height;
if (alReadyVisitedFlag[w][h] == false) {
alReadyVisitedFlag[w][h] = true;
predecessor[w][h] = head;
queue.addToBackOfQueue(connected[i]);
if (equals(connected[i], goalLoc)) {
success = true;
break outer; // we are done
}
}
}

We have processed the location at the front of the queue (in the variable head), so
remove it:

queue.removeFromFrontOfQueue();
}

Now that we are out of the main loop, we need to use the predecessor array to get
the shortest path. Note that we fill in the searchP ath array in reverse order, starting
with the goal location:

maxDepth = 0;
if (success) {
searchPath[maxDepth++] = goalLoc;

12
2.3 Finding Paths in Graphs

for (int i=0; i<100; i++) {


searchPath[maxDepth] =
predecessor[searchPath[maxDepth - 1].
width][searchPath[maxDepth - 1].
height];
maxDepth++;
if (equals(searchPath[maxDepth - 1],
startLoc))
break; // back to starting node
}
}
}

Figure 2.4 shows a good path solution between starting and goal nodes. Starting
from the initial position, the breadth first search engine adds all possible moves to
the back of a queue data structure. For each possible move added to this queue
in one search cycle, all possible moves are added to the queue for each new move
recorded. Visually, think of possible moves added to the queue as “fanning out” like
a wave from the starting location. The breadth first search engine stops when this
“wave” reaches the goal location. In general, I prefer breadth first search techniques
to depth first search techniques when memory storage for the queue used in the
search process is not an issue. In general, the memory requirements for performing
depth first search is much less than breadth first search.

To run the two example programs from this section, change directory to src/search/-
maze and type:

javac *.java
java MazeDepthFirstSearch
java MazeBreadthFirstSearch

Note that the classes M azeDepthF irstSearch and M azeBreadthF irstSearch


are simple Java JFC applications that produced Figures 2.3 and 2.4. The interested
reader can read through the source code for the GUI test programs, but we will only
cover the core AI code in this book. If you are interested in the GUI test programs
and you are not familiar with the Java JFC (or Swing) classes, there are several good
tutorials on JFC programming at java.sun.com.

2.3 Finding Paths in Graphs

In the last section, we used both depth first and breadth first search techniques to find
a path between a starting location and a goal location in a maze. Another common

13
2 Search

Figure 2.4: Using breadth first search in a maze to find an optimal solution

type of search space is represented by a graph. A graph is a set of nodes and links.
We characterize nodes as containing the following data:

• A name and/or other data

• Zero or more links to other nodes

• A position in space (this is optional, usually for display or visualization pur-


poses)

Links between nodes are often called edges. The algorithms used for finding paths
in graphs are very similar to finding paths in a two-dimensional maze. The primary
difference is the operators that allow us to move from one node to another. In the last
section we saw that in a maze, an agent can move from one grid space to another if
the target space is empty. For graph search, a movement operator allows movement
to another node if there is a link to the target node.

Figure 2.5 shows the UML class diagram for the graph search Java classes that
we will use in this section. The abstract class AbstractGraphSearch class is the
base class for both DepthF irstSearch and BreadthF irstSearch. The classes
GraphDepthF irstSearch and GraphBreadthF irstSearch and test programs
also provide a Java Foundation Class (JFC) or Swing based user interface. These
two test programs produced Figures 2.6 and 2.7.

14
2.3 Finding Paths in Graphs

AbstractGraphSearch
#getNodeIndex(String name): int
getNodeName(int index): String
addNode(String name, int x, int y): void
getNodeName(int index): String
getNodeX(int index): int
getNodeY(int index): int
getLink1(int index): int
getLink2(int index): int
addLink(int node1, int node2)k: void
findPath: int[]

DepthFIrstSearch BreadthFIrstSearch
findPath(int start_node, findPath(int start_node,
int goal_node): int[] int goal_node): int[]
1 1

GraphDepthFirstSearch GraphDepthFirstSearch
main(String[] args): void main(String[] args): void
paintNode(Graphics g, paintNode(Graphics g,
String name, String name,
int x, int y): void int x, int y): void
paint(Graphics g): void paint(Graphics g): void

Java main test


programs using JFC

Figure 2.5: UML class diagram for the graph search classes

15
2 Search

As seen in Figure 2.5, most of the data for the search operations (i.e., nodes, links,
etc.) is defined in the abstract class AbstractGraphSearch. This abstract class is
customized through inheritance to use a stack for storing possible moves (i.e., the
array path) for depth first search and a queue for breadth first search.

The abstract class AbstractGraphSearch allocates data required by both derived


classes:

final public static int MAX = 50;


protected int [] path =
new int[AbstractGraphSearch.MAX];
protected int num_path = 0;
// for nodes:
protected String [] nodeNames =
new String[MAX];
protected int [] node_x = new int[MAX];
protected int [] node_y = new int[MAX];
// for links between nodes:
protected int [] link_1 = new int[MAX];
protected int [] link_2 = new int[MAX];
protected int [] lengths = new int[MAX];
protected int numNodes = 0;
protected int numLinks = 0;
protected int goalNodeIndex = -1,
startNodeIndex = -1;

The abstract base class also provides several common utility methods:

• addNode(String name, int x, int y) – adds a new node

• addLink(int n1, int n2) – adds a bidirectional link between nodes indexed by
n1 and n2. Node indexes start at zero and are in the order of calling addNode.

• addLink(String n1, String n2) – adds a bidirectional link between nodes spec-
ified by their names

• getNumNodes() – returns the number of nodes

• getNumLinks() – returns the number of links

• getNodeName(int index) – returns a node’s name

• getNodeX(), getNodeY() – return the coordinates of a node

• getNodeIndex(String name) – gets the index of a node, given its name

16
2.3 Finding Paths in Graphs

The abstract base class defines an abstract method f indP ath that must be overrid-
den. We will start with the derived class DepthF irstSearch, looking at its im-
plementation of findPath. The f indP ath method returns an array of node indices
indicating the calculated path:

public int [] findPath(int start_node,


int goal_node) {

The class variable path is an array that is used for temporary storage; we set the first
element to the starting node index, and call the utility method f indP athHelper:

path[0] = start_node; // the starting node


return findPathHelper(path, 1, goal_node);
}

The method findPathHelper is the interesting method in this class that actually per-
forms the depth first search; we will look at it in some detail:

The path array is used as a stack to keep track of which nodes are being visited
during the search. The argument num path is the number of locations in the path,
which is also the search depth:

public int [] findPathHelper(int [] path,


int num_path,
int goal_node) {

First, re-check to see if we have reached the goal node; if we have, make a new array
of the current size and copy the path into it. This new array is returned as the value
of the method:

if (goal_node == path[num_path - 1]) {


int [] ret = new int[num_path];
for (int i=0; i<num_path; i++) {
ret[i] = path[i];
}
return ret; // we are done!
}

We have not found the goal node, so call the method connected nodes to find all
nodes connected to the current node that are not already on the search path (see the
source code for the implementation of connected nodes):

17
2 Search

int [] new_nodes = connected_nodes(path,


num_path);

If there are still connected nodes to search, add the next possible “node to visit” to
the top of the stack (variable path in the program) and recursively call the method
f indP athHelper again:

if (new_nodes != null) {
for (int j=0; j<new_nodes.length; j++) {
path[num_path] = new_nodes[j];
int [] test = findPathHelper(new_path,
num_path + 1,
goal_node);
if (test != null) {
if (test[test.length-1] == goal_node) {
return test;
}
}
}
}

If we have not found the goal node, return null, instead of an array of node indices:

return null;
}

Derived class BreadthF irstSearch also must define abstract method f indP ath.
This method is very similar to the breadth first search method used for finding a path
in a maze: a queue is used to store possible moves. For a maze, we used a queue
class that stored instances of the class Dimension, so for this problem, the queue
only needs to store integer node indices. The return value of f indP ath is an array
of node indices that make up the path from the starting node to the goal.

public int [] findPath(int start_node,


int goal_node) {

We start by setting up a flag array alreadyV isited to prevent visiting the same node
twice, and allocating a predecessors array that we will use to find the shortest path
once the goal is reached:

// data structures for depth first search:

18
2.3 Finding Paths in Graphs

boolean [] alreadyVisitedFlag =
new boolean[numNodes];
int [] predecessor = new int[numNodes];

The class IntQueue is a private class defined in the file BreadthFirstSearch.java; it


implements a standard queue:

IntQueue queue = new IntQueue(numNodes + 2);

Before the main loop, we need to initialize the already visited and predecessor ar-
rays, set the visited flag for the starting node to true, and add the starting node index
to the back of the queue:

for (int i=0; i<numNodes; i++) {


alreadyVisitedFlag[i] = false;
predecessor[i] = -1;
}
alreadyVisitedFlag[start_node] = true;
queue.addToBackOfQueue(start_node);

The main loop runs until we find the goal node or the search queue is empty:

outer: while (queue.isEmpty() == false) {

We will read (without removing) the node index at the front of the queue and calcu-
late the nodes that are connected to the current node (but not already on the visited
list) using the connected nodes method (the interested reader can see the imple-
mentation in the source code for this class):

int head = queue.peekAtFrontOfQueue();


int [] connected = connected_nodes(head);
if (connected != null) {

If each node connected by a link to the current node has not already been visited, set
the predecessor array and add the new node index to the back of the search queue;
we stop if the goal is found:

for (int i=0; i<connected.length; i++) {


if (alreadyVisitedFlag[connected[i]] == false) {
predecessor[connected[i]] = head;

19
2 Search

queue.addToBackOfQueue(connected[i]);
if (connected[i] == goal_node) break outer;
}
}
alreadyVisitedFlag[head] = true;
queue.removeFromQueue(); // ignore return value
}
}

Now that the goal node has been found, we can build a new array of returned node
indices for the calculated path using the predecessor array:

int [] ret = new int[numNodes + 1];


int count = 0;
ret[count++] = goal_node;
for (int i=0; i<numNodes; i++) {
ret[count] = predecessor[ret[count - 1]];
count++;
if (ret[count - 1] == start_node) break;
}
int [] ret2 = new int[count];
for (int i=0; i<count; i++) {
ret2[i] = ret[count - 1 - i];
}
return ret2;
}

In order to run both the depth first and breadth first graph search examples, change
directory to src-search-maze and type the following commands:

javac *.java
java GraphDepthFirstSearch
java GraphBeadthFirstSearch

Figure 2.6 shows the results of finding a route from node 1 to node 9 in the small test
graph. Like the depth first results seen in the maze search, this path is not optimal.

Figure 2.7 shows an optimal path found using a breadth first search. As we saw in
the maze search example, we find optimal solutions using breadth first search at the
cost of extra memory required for the breadth first search.

20
2.3 Finding Paths in Graphs

Figure 2.6: Using depth first search in a sample graph

Figure 2.7: Using breadth first search in a sample graph

21
2 Search

2.4 Adding Heuristics to Breadth First Search

We can usually make breadth first search more efficient by ordering the search order
for all branches from a given position in the search space. For example, when adding
new nodes from a specified reference point in the search space, we might want to
add nodes to the search queue first that are “in the direction” of the goal location: in
a two-dimensional search like our maze search, we might want to search connected
grid cells first that were closest to the goal grid space. In this case, pre-sorting nodes
(in order of closest distance to the goal) added to the breadth first search queue could
have a dramatic effect on search efficiency. In the next chapter we will build a simple
real-time planning system around our breadth first maze search program; this new
program will use heuristics. The alpha-beta additions to breadth first search are seen
in in the next section.

2.5 Search and Game Playing

Now that a computer program has won a match against the human world champion,
perhaps people’s expectations of AI systems will be prematurely optimistic. Game
search techniques are not real AI, but rather, standard programming techniques. A
better platform for doing AI research is the game of Go. There are so many possible
moves in the game of Go that brute force look ahead (as is used in Chess playing
programs) simply does not work.

That said, min-max type search algorithms with alpha-beta cutoff optimizations are
an important programming technique and will be covered in some detail in the re-
mainder of this chapter. We will design an abstract Java class library for imple-
menting alpha-beta enhanced min-max search, and then use this framework to write
programs to play tic-tac-toe and chess.

2.5.1 Alpha-Beta Search

The first game that we will implement will be tic-tac-toe, so we will use this simple
game to explain how the min-max search (with alpha-beta cutoffs) works.

Figure 2.8 shows the possible moves generated from a tic-tac-toe position where X
has made three moves and O has made two moves; it is O’s turn to move. This is
“level 0” in Figure 2.8. At level 0, O has four possible moves. How do we assign
a fitness value to each of O’s possible moves at level 0? The basic min-max search
algorithm provides a simple solution to this problem: for each possible move by O
in level 1, make the move and store the resulting 4 board positions. Now, at level 1,
it is X’s turn to move. How do we assign values to each of X’s possible three moves

22
2.5 Search and Game Playing

O X O
X
Level 0:
O to move

O X O O X O O X O O X O
X X O
Level 1:
O X X
X O X O X X X to move

O XO O X O O X O Level 2:
O X O X O X X O to move
X X X X X

Figure 2.8: Alpha-beta algorithm applied to part of a game of tic-tac-toe

in Figure 2.8? Simple, we continue to search by making each of X’s possible moves
and storing each possible board position for level 2. We keep recursively applying
this algorithm until we either reach a maximum search depth, or there is a win, loss,
or draw detected in a generated move. We assume that there is a fitness function
available that rates a given board position relative to either side. Note that the value
of any board position for X is the negative of the value for O.

To make the search more efficient, we maintain values for alpha and beta for each
search level. Alpha and beta determine the best possible/worst possible move avail-
able at a given level. If we reach a situation like the second position in level 2 where
X has won, then we can immediately determine that O’s last move in level 1 that
produced this position (of allowing X an instant win) is a low valued move for O
(but a high valued move for X). This allows us to immediately “prune” the search
tree by ignoring all other possible positions arising from the first O move in level
1. This alpha-beta cutoff (or tree pruning) procedure can save a large percentage of
search time, especially if we can set the search order at each level with “probably
best” moves considered first.

While tree diagrams as seen in Figure 2.8 quickly get complicated, it is easy for a
computer program to generate possible moves, calculate new possible board posi-
tions and temporarily store them, and recursively apply the same procedure to the
next search level (but switching min-max “sides” in the board evaluation). We will
see in the next section that it only requires about 100 lines of Java code to implement
an abstract class framework for handling the details of performing an alpha-beta en-
hanced search. The additional game specific classes for tic-tac-toe require about an
additional 150 lines of code to implement; chess requires an additional 450 lines of
code.

23
2 Search

2.5.2 A Java Framework for Search and Game Playing

The general interface for the Java classes that we will develop in this section was
inspired by the Common LISP game-playing framework written by Kevin Knight
and described in (Rich, Knight 1991). The abstract class GameSearch contains the
code for running a two-player game and performing an alpha-beta search. This class
needs to be sub-classed to provide the eight methods:

public abstract boolean drawnPosition(Position p)


public abstract boolean wonPosition(Position p,
boolean player)
positionEvaluation(Position p,
boolean player)
public abstract void printPosition(Position p)
public abstract Position []
possibleMoves(Position p,
boolean player)
public abstract Position makeMove(Position p,
boolean player,
Move move)
public abstract boolean reachedMaxDepth(Position p,
int depth)
public abstract Move getMove()

The method drawnP osition should return a Boolean true value if the given po-
sition evaluates to a draw situation. The method wonP osition should return a
true value if the input position is won for the indicated player. By convention, I
use a Boolean true value to represent the computer and a Boolean false value to
represent the human opponent. The method positionEvaluation returns a posi-
tion evaluation for a specified board position and player. Note that if we call po-
sitionEvaluation switching the player for the same board position, then the value
returned is the negative of the value calculated for the opposing player. The method
possibleM oves returns an array of objects belonging to the class Position. In an
actual game like chess, the position objects will actually belong to a chess-specific
refinement of the Position class (e.g., for the chess program developed later in this
chapter, the method possibleM oves will return an array of ChessP osition ob-
jects). The method makeM ove will return a new position object for a specified
board position, side to move, and move. The method reachedM axDepth returns
a Boolean true value if the search process has reached a satisfactory depth. For the
tic-tac-toe program, the method reachedM axDepth does not return true unless ei-
ther side has won the game or the board is full; for the chess program, the method
reachedM axDepth returns true if the search has reached a depth of 4 half moves
deep (this is not the best strategy, but it has the advantage of making the example

24
2.5 Search and Game Playing

program short and easy to understand). The method getM ove returns an object of a
class derived from the class M ove (e.g., T icT acT oeM ove or ChessM ove).

The GameSearch class implements the following methods to perform game search:

protected Vector alphaBeta(int depth, Position p,


boolean player)
protected Vector alphaBetaHelper(int depth,
Position p,
boolean player,
float alpha,
float beta)
public void playGame(Position startingPosition,
boolean humanPlayFirst)

The method alphaBeta is simple; it calls the helper method alphaBetaHelper


with initial search conditions; the method alphaBetaHelper then calls itself recur-
sively. The code for alphaBeta is:

protected Vector alphaBeta(int depth,


Position p,
boolean player) {
Vector v = alphaBetaHelper(depth, p, player,
1000000.0f,
-1000000.0f);
return v;
}

It is important to understand what is in the vector returned by the methods alphaBeta


and alphaBetaHelper. The first element is a floating point position evaluation for
the point of view of the player whose turn it is to move; the remaining values are the
“best move” for each side to the last search depth. As an example, if I let the tic-tac-
toe program play first, it places a marker at square index 0, then I place my marker
in the center of the board an index 4. At this point, to calculate the next computer
move, alphaBeta is called and returns the following elements in a vector:

next element: 0.0


next element: [-1,0,0,0,1,0,0,0,0,]
next element: [-1,1,0,0,1,0,0,0,0,]
next element: [-1,1,0,0,1,0,0,-1,0,]
next element: [-1,1,0,1,1,0,0,-1,0,]
next element: [-1,1,0,1,1,-1,0,-1,0,]
next element: [-1,1,1,1,1,-1,0,-1,0,]

25
2 Search

next element: [-1,1,1,1,1,-1,-1,-1,0,]


next element: [-1,1,1,1,1,-1,-1,-1,1,]

Here, the alpha-beta enhanced min-max search looked all the way to the end of the
game and these board positions represent what the search procedure calculated as
the best moves for each side. Note that the class T icT acT oeP osition (derived
from the abstract class P osition) has a toString method to print the board values to
a string.

The same printout of the returned vector from alphaBeta for the chess program is:

next element: 5.4


next element:
[4,2,3,5,9,3,2,4,7,7,1,1,1,0,1,1,1,1,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,1,0,0,0,0,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,0,-1,0,0,0,7,7,
-1,-1,-1,-1,0,-1,-1,-1,7,7,-4,-2,-3,-5,-9,
-3,-2,-4,]
next element:
[4,2,3,0,9,3,2,4,7,7,1,1,1,5,1,1,1,1,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,1,0,0,0,0,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,0,-1,0,0,0,7,7,
-1,-1,-1,-1,0,-1,-1,-1,7,7,-4,-2,-3,-5,-9,
-3,-2,-4,]
next element:
[4,2,3,0,9,3,2,4,7,7,1,1,1,5,1,1,1,1,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,1,0,0,0,0,7,7,
0,0,0,0,0,0,0,0,7,7,0,0,0,0,-1,-5,0,0,7,7,
-1,-1,-1,-1,0,-1,-1,-1,7,7,-4,-2,-3,0,-9,
-3,-2,-4,]
next element:
[4,2,3,0,9,3,0,4,7,7,1,1,1,5,1,1,1,1,7,7,
0,0,0,0,0,2,0,0,7,7,0,0,0,1,0,0,0,0,7,7,
0,0,0,,0,0,0,0,0,7,7,0,0,0,0,-1,-5,0,0,7,7,
-1,-1,-1,-1,0,-1,-1,-1,7,7,-4,-2,-3,0,-9,
-3,-2,-4,]
next element:
[4,2,3,0,9,3,0,4,7,7,1,1,1,5,1,1,1,1,7,7,
0,0,0,0,0,2,0,0,7,7,0,0,0,1,0,0,0,0,7,7,
-1,0,0,0,0,0,0,0,7,7,0,0,0,0,-1,-5,0,0,7,7,
0,-1,-1,-1,0,-1,-1,-1,7,7,-4,-2,-3,0,-9,
-3,-2,-4,]

26
2.5 Search and Game Playing

Here, the search procedure assigned the side to move (the computer) a position
evaluation score of 5.4; this is an artifact of searching to a fixed depth. Notice that
the board representation is different for chess, but because the GameSearch class
manipulates objects derived from the classes P osition and M ove, the GameSearch
class does not need to have any knowledge of the rules for a specific game. We will
discuss the format of the chess position class ChessP osition in more detail when
we develop the chess program.

The classes Move and Position contain no data and methods at all. The classes
Move and Position are used as placeholders for derived classes for specific games.
The search methods in the abstract GameSearch class manipulate objects derived
from the classes Move and Position.

Now that we have seen the debug printout of the contents of the vector returned from
the methods alphaBeta and alphaBetaHelper, it will be easier to understand how
the method alphaBetaHelper works. The following text shows code fragments
from the alphaBetaHelper method interspersed with book text:

protected Vector alphaBetaHelper(int depth,


Position p,
boolean player,
float alpha,
float beta) {

Here, we notice that the method signature is the same as for alphaBeta, except that
we pass floating point alpha and beta values. The important point in understanding
min-max search is that most of the evaluation work is done while “backing up”
the search tree; that is, the search proceeds to a leaf node (a node is a leaf if the
method reachedM axDepth return a Boolean true value), and then a return vector
for the leaf node is created by making a new vector and setting its first element to the
position evaluation of the position at the leaf node and setting the second element of
the return vector to the board position at the leaf node:

if (reachedMaxDepth(p, depth)) {
Vector v = new Vector(2);
float value = positionEvaluation(p, player);
v.addElement(new Float(value));
v.addElement(p);
return v;
}

If we have not reached the maximum search depth (i.e., we are not yet at a leaf node
in the search tree), then we enumerate all possible moves from the current position
using the method possibleM oves and recursively call alphaBetaHelper for each

27
2 Search

new generated board position. In terms of Figure 2.8, at this point we are moving
down to another search level (e.g., from level 1 to level 2; the level in Figure 2.8
corresponds to depth argument in alphaBetaHelper):

Vector best = new Vector();


Position [] moves = possibleMoves(p, player);
for (int i=0; i<moves.length; i++) {
Vector v2 = alphaBetaHelper(depth + 1, moves[i],
!player,
-beta, -alpha);
float value = -((Float)v2.elementAt(0)).floatValue();
if (value > beta) {
if(GameSearch.DEBUG)
System.out.println(" ! ! ! value="+
value+
",beta="+beta);
beta = value;
best = new Vector();
best.addElement(moves[i]);
Enumeration enum = v2.elements();
enum.nextElement(); // skip previous value
while (enum.hasMoreElements()) {
Object o = enum.nextElement();
if (o != null) best.addElement(o);
}
}
/**
* Use the alpha-beta cutoff test to abort
* search if we found a move that proves that
* the previous move in the move chain was dubious
*/
if (beta >= alpha) {
break;
}
}

Notice that when we recursively call alphaBetaHelper, we are “flipping” the


player argument to the opposite Boolean value. After calculating the best move
at this depth (or level), we add it to the end of the return vector:

Vector v3 = new Vector();


v3.addElement(new Float(beta));
Enumeration enum = best.elements();

28
2.5 Search and Game Playing

while (enum.hasMoreElements()) {
v3.addElement(enum.nextElement());
}
return v3;

When the recursive calls back up and the first call to alphaBetaHelper returns a
vector to the method alphaBeta, all of the “best” moves for each side are stored
in the return vector, along with the evaluation of the board position for the side to
move.

The class GameSearch method playGame is fairly simple; the following code
fragment is a partial listing of playGame showing how to call alphaBeta, getM ove,
and makeM ove:

public void playGame(Position startingPosition,


boolean humanPlayFirst) {
System.out.println("Your move:");
Move move = getMove();
startingPosition = makeMove(startingPosition,
HUMAN, move);
printPosition(startingPosition);
Vector v = alphaBeta(0, startingPosition, PROGRAM);
startingPosition = (Position)v.elementAt(1);
}
}

The debug printout of the vector returned from the method alphaBeta seen earlier
in this section was printed using the following code immediately after the call to the
method alphaBeta:

Enumeration enum = v.elements();


while (enum.hasMoreElements()) {
System.out.println(" next element: " +
enum.nextElement());
}

In the next few sections, we will implement a tic-tac-toe program and a chess-
playing program using this Java class framework.

2.5.3 Tic-Tac-Toe Using the Alpha-Beta Search Algorithm

Using the Java class framework of GameSearch, P osition, and M ove, it is simple
to write a basic tic-tac-toe program by writing three new derived classes (see Figure

29
2 Search

Move Position GameSearch


+DEBUG: boolean
+PROGRAM: boolean
+HUMAN: boolean
TicTacToePosition #alphaBeta: ArrayList
TicTacToeMove +BLANK: int #alphaBetaHelper: ArrayList
+moveIndex: int +HUMAN: int +playGame: void
+PROGRAM: int
board: int[]
+toString: String
TicTacToe

+drawnPosition: boolean
+wonPosition: boolean
-winCheck: boolean
+positionEvaluation: float
+printPosition: void
+possibleMoves: Position[]
+makeMove: Position
+reachedMaxDepth: boolean
+main: void
+move: Move

Figure 2.9: UML class diagrams for game search engine and tic-tac-toe

2.9) T icT acT oe (derived from GameSearch), T icT acT oeM ove (derived from
M ove), and T icT acT oeP osition (derived from P osition).

I assume that the reader has the book example code installed and available for view-
ing. In this section, I will only discuss the most interesting details of the tic-tac-
toe class refinements; I assume that the reader can look at the source code. We
will start by looking at the refinements for the position and move classes. The
T icT acT oeM ove class is trivial, adding a single integer value to record the square
index for the new move:

public class TicTacToeMove extends Move {


public int moveIndex;
}

The board position indices are in the range of [0..8] and can be considered to be in
the following order:

0 1 2
3 4 5
6 7 8

The class T icT acT oeP osition is also simple:

30
2.5 Search and Game Playing

public class TicTacToePosition extends Position {


final static public int BLANK = 0;
final static public int HUMAN = 1;
final static public int PROGRAM = -1;
int [] board = new int[9];
public String toString() {
StringBuffer sb = new StringBuffer("[");
for (int i=0; i<9; i++)
sb.append(""+board[i]+",");
sb.append("]");
return sb.toString();
}
}

This class allocates an array of nine integers to represent the board, defines constant
values for blank, human, and computer squares, and defines a toString method to
print out the board representation to a string.

The T icT acT oe class must define the following abstract methods from the base
class GameSearch:

public abstract boolean drawnPosition(Position p)


public abstract boolean wonPosition(Position p,
boolean player)
public abstract float positionEvaluation(Position p,
boolean player)
public abstract void printPosition(Position p)
public abstract Position [] possibleMoves(Position p,
boolean player)
public abstract Position makeMove(Position p,
boolean player,
Move move)
public abstract boolean reachedMaxDepth(Position p,
int depth)
public abstract Move getMove()

The implementation of these methods uses the refined classes T icT acT oeM ove
and T icT acT oeP osition. For example, consider the method drawnP osition that
is responsible for selecting a drawn (or tied) position:

public boolean drawnPosition(Position p) {


boolean ret = true;
TicTacToePosition pos = (TicTacToePosition)p;

31
2 Search

for (int i=0; i<9; i++) {


if (pos.board[i] == TicTacToePosition.BLANK){
ret = false;
break;
}
}
return ret;
}

The overridden methods from the GameSearch base class must always cast argu-
ments of type P osition and M ove to T icT acT oeP osition and T icT acT oeM ove.
Note that in the method drawnP osition, the argument of class P osition is cast to
the class T icT acT oeP osition. A position is considered to be a draw if all of the
squares are full. We will see that checks for a won position are always made be-
fore checks for a drawn position, so that the method drawnP osition does not need
to make a redundant check for a won position. The method wonP osition is also
simple; it uses a private helper method winCheck to test for all possible winning
patterns in tic-tac-toe. The method positionEvaluation uses the following board
features to assign a fitness value from the point of view of either player:

The number of blank squares on the board


If the position is won by either side
If the center square is taken

The method positionEvaluation is simple, and is a good place for the interested
reader to start modifying both the tic-tac-toe and chess programs:

public float positionEvaluation(Position p,


boolean player) {
int count = 0;
TicTacToePosition pos = (TicTacToePosition)p;
for (int i=0; i<9; i++) {
if (pos.board[i] == 0) count++;
}
count = 10 - count;
// prefer the center square:
float base = 1.0f;
if (pos.board[4] == TicTacToePosition.HUMAN &&
player) {
base += 0.4f;
}
if (pos.board[4] == TicTacToePosition.PROGRAM &&
!player) {

32
2.5 Search and Game Playing

base -= 0.4f;
}
float ret = (base - 1.0f);
if (wonPosition(p, player)) {
return base + (1.0f / count);
}
if (wonPosition(p, !player)) {
return -(base + (1.0f / count));
}
return ret;
}

The only other method that we will look at here is possibleM oves; the interested
reader can look at the implementation of the other (very simple) methods in the
source code. The method possibleM oves is called with a current position, and the
side to move (i.e., program or human):

public Position [] possibleMoves(Position p,


boolean player) {
TicTacToePosition pos = (TicTacToePosition)p;
int count = 0;
for (int i=0; i<9; i++) {
if (pos.board[i] == 0) count++;
}
if (count == 0) return null;
Position [] ret = new Position[count];
count = 0;
for (int i=0; i<9; i++) {
if (pos.board[i] == 0) {
TicTacToePosition pos2 =
new TicTacToePosition();
for (int j=0; j<9; j++)
pos2.board[j] = pos.board[j];
if (player) pos2.board[i] = 1;
else pos2.board[i] = -1;
ret[count++] = pos2;
}
}
return ret;
}

It is very simple to generate possible moves: every blank square is a legal move.
(This method will not be as straightforward in the example chess program!)

33
2 Search

It is simple to compile and run the example tic-tac-toe program: change directory to
src-search-game and type:

javac *.java
java TicTacToe

When asked to enter moves, enter an integer between 0 and 8 for a square that is
currently blank (i.e., has a zero value). The following shows this labeling of squares
on the tic-tac-toe board:

0 1 2
3 4 5
6 7 8

2.5.4 Chess Using the Alpha-Beta Search Algorithm

Using the Java class framework of GameSearch, P osition, and M ove, it is rea-
sonably easy to write a simple chess program by writing three new derived classes
(see Figure 2.10) Chess (derived from GameSearch), ChessM ove (derived from
M ove), and ChessP osition (derived from P osition). The chess program devel-
oped in this section is intended to be an easy to understand example of using alpha-
beta min-max search; as such, it ignores several details that a fully implemented
chess program would implement:

• Allow the computer to play either side (computer always plays black in this
example).

• Allow en-passant pawn captures.

• Allow the player to take back a move after making a mistake.

The reader is assumed to have read the last section on implementing the tic-tac-
toe game; details of refining the GameSearch, Move, and Position classes are not
repeated in this section.

Figure 2.10 shows the UML class diagram for both the general purpose GameSearch
framework and the classes derived to implement chess specific data and behavior.

The class ChessMove contains data for recording from and to square indices:

public class ChessMove extends Move {


public int from;
public int to;
}

34
2.5 Search and Game Playing

Move Position GameSearch


+DEBUG: boolean
+PROGRAM: boolean
ChessMove ChessPosition +HUMAN: boolean
+from: int +BLANK: int #alphaBeta: ArrayList
+to: int +HUMAN: int #alphaBetaHelper: ArrayList
+PROGRAM: int +playGame: void
+PAWN: int
+KNIGHT: int
+BISHOP: int Chess
+ROOK: int -computerControl: float[]
+QUEEN: int -humanControl: float[]
+KING: int -possibleMoveList: Move[]
board: int[] -piece_moves: int[]
+toString: String -initialBoard: int[]
-index: int[]
-pieceMovementTable: int[]
-value: int[]
+drawnPosition: boolean
+wonPosition: boolean
-winCheck: boolean
+positionEvaluation: float
+printPosition: void
+possibleMoves: Position[]
+makeMove: Position
+reachedMaxDepth: boolean
+main: void
-setControlData: void
-calcPossibleMoves: int
-calcPieceMoves: int
+move: Move

Figure 2.10: UML class diagrams for game search engine and chess

35
2 Search

1 c4 b6 2 d4 Bb7 Black increases the mobility of its


pieces by fianchettoing the queenside bishop:

8
rm0lkans
7
obopopop
6
0o0Z0Z0Z
5
Z0Z0Z0Z0
4
0ZPO0Z0Z
3
Z0Z0Z0Z0
2
PO0ZPOPO
1
SNAQJBMR
a b c d e f g h

Figure 2.11: The example chess program does not contain an opening book so it
plays to maximize the mobility of its pieces and maximize material
advantage using a two-move lookahead. The first version of the chess
program contains a few heuristics like wanting to control the center
four squares.

The board is represented as an integer array with 120 elements. A chessboard only
has 64 squares; the remaining board values are set to a special value of 7, which
indicates an “off board” square. The initial board setup is defined statically in the
Chess class and the off-board squares have a value of “7”:

private static int [] initialBoard = {


7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
4, 2, 3, 5, 9, 3, 2, 4, 7, 7, // white pieces
1, 1, 1, 1, 1, 1, 1, 1, 7, 7, // white pawns
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, // 8 blank squares
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, // 8 blank squares
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, // 8 blank squares
0, 0, 0, 0, 0, 0, 0, 0, 7, 7, // 8 blank squares
-1,-1,-1,-1,-1,-1,-1,-1, 7, 7, // black pawns
-4,-2,-3,-5,-9,-3,-2,-4, 7, 7, // black pieces
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
};

It is difficult to see from this listing of the board square values but in effect a regular
chess board if padded on all sides with two rows and columns of “7” values.

36
2.5 Search and Game Playing

3 Nf3 g6 4 Bf4 Bg7 5 Nc3 Black (the computer)


continues to increase piece mobility and control the center
squares:

8
rm0lkZns
7
obopopap
6
0o0Z0ZpZ
5
Z0Z0Z0Z0
4
0ZPO0A0Z
3
Z0M0ZNZ0
2
PO0ZPOPO
1
S0ZQJBZR
a b c d e f g h

Figure 2.12: Continuing the first sample game: the computer is looking ahead two
moves and no opening book is used.

We see the start of a sample chess game in Figure 2.11 and the continuation of this
same game in Figure 2.12.The lookahead is limited to 2 moves (4 ply).

The class ChessP osition contains data for this representation and defines constant
values for playing sides and piece types:

public class ChessPosition extends Position {


final static public int BLANK = 0;
final static public int HUMAN = 1;
final static public int PROGRAM = -1;
final static public int PAWN = 1;
final static public int KNIGHT = 2;
final static public int BISHOP = 3;
final static public int ROOK = 4;
final static public int QUEEN = 5;
final static public int KING = 6;
int [] board = new int[120];
public String toString() {
StringBuffer sb = new StringBuffer("[");
for (int i=22; i<100; i++) {
sb.append(""+board[i]+",");
}
sb.append("]");
return sb.toString();
}

37
2 Search

The class Chess also defines other static data. The following array is used to encode
the values assigned to each piece type (e.g., pawns are worth one point, knights and
bishops are worth 3 points, etc.):

private static int [] value = {


0, 1, 3, 3, 5, 9, 0, 0, 0, 12
};

The following array is used to codify the possible incremental moves for pieces:

private static int [] pieceMovementTable = {


0, -1, 1, 10, -10, 0, -1, 1, 10, -10, -9, -11, 9,
11, 0, 8, -8, 12, -12, 19, -19, 21, -21, 0, 10, 20,
0, 0, 0, 0, 0, 0, 0, 0
};

The starting index into the pieceMovementTable array is calculated by indexing the
following array with the piece type index (e.g., pawns are piece type 1, knights are
piece type 2, bishops are piece type 3, rooks are piece type 4, etc.:

private static int [] index = {


0, 12, 15, 10, 1, 6, 0, 0, 0, 6
};

When we implement the method possibleM oves for the class Chess, we will see
that except for pawn moves, all other possible piece type moves are very easy to
calculate using this static data. The method possibleM oves is simple because it
uses a private helper method calcP ieceM oves to do the real work. The method
possibleM oves calculates all possible moves for a given board position and side to
move by calling calcP ieceM ove for each square index that references a piece for
the side to move.

We need to perform similar actions for calculating possible moves and squares that
are controlled by each side. In the first version of the class Chess that I wrote, I used
a single method for calculating both possible move squares and controlled squares.
However, the code was difficult to read, so I split this initial move generating method
out into three methods:

• possibleMoves – required because this was an abstract method in Game-


Search. This method calls calcPieceMoves for all squares containing pieces
for the side to move, and collects all possible moves.

38
Other documents randomly have
different content
„Weil heute Sonntag ist,“ entschuldigte sich Agnes, da Pfanner ihr
neuerdings Verschwendung vorwarf.
Es war ein Verdacht in ihm rege geworden, den er nicht
aussprach, der ihn aber quälte, und der entweder getilgt oder
gerechtfertigt werden mußte. Kürzlich hatte er sich um
Lebensmittelpreise erkundigt, hatte gerechnet und herausgebracht,
daß die Ausgaben, die sich seine Frau fortgesetzt erlaubte,
unmöglich mit dem ihr zur Verfügung gestellten Küchengelde
bestritten werden konnten. Erarbeitet wollte sie den Überschuß
haben? Lächerlich! Er, der Sohn einer armen Näherin, wußte, was
seine Mutter verdient hatte mit täglich zwölfstündiger emsiger
Arbeit. Ihm ins Gesicht sollte seine Frau, die ihren Haushalt ohne
jegliche Unterstützung bestellte, nicht behaupten, daß sie imstande
sei, sich eine regelmäßige Einnahme zu verschaffen. Womit also
bestritt sie die Mehrauslagen? Pfanner begnügte sich nicht lange mit
den ausweichenden Antworten, die sie ihm gab. Eines Tages stellte
er ein scharfes Verhör an, und sie, in die Enge getrieben, angeekelt
von der erniedrigenden Pein, immer neue Ausflüchte ersinnen zu
sollen – gestand.
Ja denn, ja, sie verkaufte, sie versetzte, sie gab ihr Letztes her,
damit das Kind, das in fortwährender geistiger Anspannung lebte,
ordentlich ernährt werde in den Jahren der Entwicklung und des
stärksten Wachsens.
Pfanner zürnte, höhnte: Was hatte denn er gehabt in diesen
selben Jahren? Wer hatte denn gefragt, wie er sich nährte? Georg
wuchs auf wie ein Hofratssohn im Vergleich zu ihm. Er, zu vierzehn
Jahren, hatte sich sein Brot selbst verdienen müssen, sein Brot im
Sinne des Wortes! und nicht etwa ein frisch gebackenes. Die
Entbehrungen hatten ihm sehr gut angeschlagen, er war immer
gesund geblieben. Warum sollte sein Bub anders geartet sein als er
und wie ein Weichling behandelt werden, den man aufpäppeln muß?
Agnes beharrte zum ersten Male während ihrer langen Ehe im
Widerstand gegen den Mann. Der Augenblick, den sie so sehr
gefürchtet hatte, war gekommen und fand sie stärker, als sie
geglaubt hatte sein zu können. Ruhig ließ sie die Anklagen Pfanners
über sich ergehen, und indes er ihr vorwarf, ihn hintergangen zu
haben, grübelte sie nach über eine Möglichkeit, ihn noch weiter zu
hintergehen. Es mußte sein, um des Kindes willen.
So widerstandsfähig, wie sein Vater gewesen, war eben der
blasse, hochaufgeschossene Junge nicht, der jetzt mit einem: „Guten
Abend, Vater und Mutter!“ eintrat und schweratmend an der Tür
stehen blieb, als ob die gewitterschwüle Atmosphäre, die im Zimmer
herrschte, ihm auf die Brust gefallen wäre.

Einige Tage später feierte Georg seinen vierzehnten Geburtstag. Er


hatte zwei Vorzugsnoten aus der Schule mitgebracht. Mit feierlichem
Ernst und mit der Mahnung, das kostbare Geschenk zu schonen,
übergab ihm sein Vater einen neuen Sommeranzug, eine hübsche
Mütze und ein Paar solide Halbschuhe. Am Nachmittag blieb Pfanner
länger als gewöhnlich am Tische sitzen und sprach, nachdem Frau
Agnes das Zimmer verlassen hatte, eingehender und zutraulicher mit
Georg, als sonst seine Art war.
Er wußte wohl, die Mutter nannte ihn grausam, und fand, daß er
zu viel verlange von seinem Sohne. Wenn es nach ihr ginge, würde
der jetzt freilich gute Tage haben, die Schule Schule sein lassen und
nur tun, was ihm gefiele. Aber dann? Wie würde die Zukunft
aussehen nach einer vertrödelten Jugend? Und ist die Zukunft nicht
die Hauptsache? Ausgerüstet mit der Macht des Wissens soll Georg
der seinen entgegengehen. Ohne Mühe freilich ist Wissen nicht zu
erringen. Will er der Feigling sein, der vor der Mühe flieht, oder der
Held, der sie aufsucht, mit ihr ringt, sie überwindet? Es gibt keinen
Sieg außer diesem ersten. Ohne ihn ist kein hohes Ziel zu erreichen.
„Das deine soll ein hohes sein!“ rief Pfanner aus. „Du bist nun kein
Kind mehr, und ich kann dir sagen, das Ziel, das du dir stecken
sollst, ist, ein Staatsmann zu werden. Einer, der mit überlegenem
Geiste und mit starker Hand die Teufel der Zwietracht, die unsre
Heimat zerreißen, bezwingt, das große Wort: ‚Gleiches Recht für alle‘
von den Lippen in die Herzen verpflanzt und es zur Tat, und uns
einig, groß und glücklich macht. Denk dir, ein Mann sein, der das
vermöchte! Er würde der Retter, der Erlöser, der Abgott seines
Volkes.“
Georg hörte ihm voll Bewunderung zu. Daß sein Vater mit ihm
redete wie mit einem Ebenbürtigen, machte ihn unendlich stolz. Der
Glaube an sich selbst, der ins Schwanken gekommen war, erwachte
wieder. „Ein ordentlicher Mensch sein, ist viel, und der mittelmäßig
Begabte mag sich damit begnügen,“ hatte der Vater unter anderm
gesagt, „ein außerordentlich Begabter ist sich selbst und den andern
schuldig, ein großer Mensch zu werden. Bei ihm kommt es nur auf
den Willen an, auf den unerschütterlichen Entschluß ...“
Er konnte nicht einschlafen an diesem Abend. Die Zukunftsbilder,
die sein Vater entworfen hatte, standen zu lebhaft vor ihm. Von der
Tätigkeit eines Staatsmannes machte er sich allerdings keinen
rechten Begriff, sah sich vorerst auf der Rednerbühne, einer
Versammlung gegenüber, die ihn mit höhnenden Zurufen empfing;
Feindseligkeit blickte aus aller Augen, in jedem Gesicht stand ein:
Nein! geschrieben. Und er begann zu sprechen, und allmählich
verstummten die Zurufe, und von den Gesichtern verschwand der
mißgünstige Ausdruck, Teilnahme und Zustimmung wurden rege und
begannen sich zu äußern, vereinzelt erst, dann immer häufiger,
endlich völlig einstimmig. Er hatte seine Zuhörer hingerissen durch
die Gewalt seines Wortes. Und alle, vom Ersten bis zum Letzten,
sahen den Führer in ihm und folgten ihm willig und entzückt; denn
sie wußten, was er wollte, war das Gute, das Weise, und der Weg,
den er sie führte, war der Weg zu ihrem Heile.
Auf seinen nächsten Gängen zur Schule blieb er nicht mehr bei
Salomon stehen. Er dankte für die freundlichen Winke und
Verbeugungen des Hausierers nur mit einem kurzen Grußwort.
Einmal hielt er sich aber doch bei ihm auf. Salomon hatte ihn gar zu
inständig flehend angesehen und fragte gar zu trübselig:
„Habe ich Ihnen was getan, junger Herr, sind Sie böse auf mich?“
„Was dir einfällt,“ erwiderte Georg, „was werd ich denn bös auf
dich sein.“
Es kam Salomon halt so vor. Vielleicht hatte die Nachtigall sich
doch nicht bewährt, hineinschauen kann man ja nicht, und vielleicht
wünschte der junge Herr eine andre. Salomon war bereit, ihm eine
andre zu geben um den halben Preis.
„Eine andre um den halben Preis,“ erwiderte Georg. Gewaltig trat
die Versuchung an ihn, den lockenden Antrag anzunehmen. Aber er
bestand, er siegte in seinem kurzen Seelenkampf.
„Nein, nein, ich brauch keine Nachtigall mehr, ich will keine!“ rief
er. „Ich bin jetzt vierzehn Jahre alt, und es gehört sich für mich nicht
mehr zu spielen. Ich muß lernen, ich muß trachten, Vorzugsschüler
zu bleiben, ich darf keinen andern Gedanken haben als lernen.“
Diesen Vorsatz führte er aus.

Es kamen Tage, an denen sein Fleiß an Raserei grenzte. Sie


verflossen und ließen eine schauderhafte Erschöpfung zurück.
Niemandem, nicht einmal seiner Mutter, vertraute er, was um diese
Zeit in ihm vorging. „Ich werd noch närrisch,“ dachte er. „In meinem
Kopf ist kein Blut und kein Hirn; in meinem Kopf ist es weiß und leer.
Das Lernen hat alles aufgefressen und muß jetzt auch aufhören, weil
es nichts mehr zu fressen findet.“ Das ist ganz natürlich und ganz
albern und ein peinigender Zustand, aus dem sich aufzuraffen
unmöglich ist ...
Wie im Halbschlaf saß er bei seinen Büchern, und eben in dieser
Zeit ließ Pepi sich herab, einer Anwandlung des Fleißes
nachzugeben, und kam ihm nach, kam ihm vor in großen Sprüngen.
Aus jedem Gegenstand, in dem er aufgerufen wurde, erhielt er eine
Vorzugsklasse.
Und wieder fragte ihn Georg: „Wie machst du's, daß du immer
weißt? Sag mir's, wie du's machst?“
Pepi steckte die Hände in die Taschen und warf die Beine, als ob
er sie von sich schleudern wollte:
„Zu langweilig!... Dumme Fragerei!“ ... In abgebrochenen Sätzen
nur geruhte er zu antworten. Sein Alter gab klein bei, weil er ihm
gedroht hatte, sich zu erschießen. So tat er ihm denn auch etwas
zulieb und legte seinem Genie keinen Kappzaum mehr an: „Und jetzt
mach ich ihm halt die Freud und werd Primus.“
„Ja, ja, wenn's geht!“
„Wenn's geht?“
„Gar gewiß ist's doch nicht. Es ist noch der Rott da und der
Bingler.“
„Ich werd Primus,“ wiederholte Pepi voll Aufgeblasenheit. „Alles
geht und wird, wie ich's haben will – grad so!“
„Wie du's haben willst?“
„Grad so. Das kannst du nicht begreifen. Du freilich nicht, du
armer Büffler. Weil du nur ein Büffler bist, kannst du's nicht
begreifen. Du möchtest nur; ich kann, was ich mag.“
Georg warf sich in die Brust: „Und ich auch,“ wollte er antworten;
doch brach ihm die Stimme ...
Ihm war, als ob der Boden sich aufrisse und zwischen ihm und
dem gottbegnadeten Kameraden ein unüberbrückbarer Abgrund
gähne. Drüben, mitten in fruchtbaren Gefilden, in denen alles grünte
und blühte, stand Pepi, und wohin sein Fuß trat, entsprang ein Quell,
und was seine Hand berührte, wurde zur herrlichen Frucht. Und er
hüben, auf kargem, steinigem Boden, der widerstrebend nur und
ungern sich den schattigen Zweig, den nährenden Halm entringen
ließ.
Warum die schreiende Ungerechtigkeit, warum dem andern alles
und ihm so bettelhaft wenig?
Pepi beobachtete seinen stillen Kampf und verzog höhnisch den
Mund. „Büffler!“ sprach er. „Büffeln kommt von Büffel, und Büffel
gehören zu der Gruppe der Rinder.“
Da ergriff wilder Zorn den sanftmütigen Georg. Er sprang auf Pepi
zu und packte ihn an der Gurgel.
Der unerwartet Angefallene brüllte und wehrte sich mit Händen
und Füßen, und bald waren die beiden umringt von einer johlenden
Schar, die sich an dem Zweikampf beteiligte, fast durchweg
zugunsten Georgs. Den vielbeneideten, vielgehaßten Pepi einmal
gänzlich überwunden abziehen zu sehen, gewährte jedem einzelnen
einen köstlichen Genuß. Jämmerlich zugerichtet, in zerfetzten
Kleidern, verließ er den Plan. Das begab sich unweit der Schule, und
an der Straßenecke war Salomon gestanden und hatte der Schlacht
mit gespannter Teilnahme zugesehen. Er begleitete Georg mit
Glückwünschen und Heilrufen; der aber winkte traurig ab. Er hatte
etwas getan, was seinem ganzen Wesen widersprach, schämte sich
seines Erfolges und betrachtete mit Entsetzen seinen neuen Rock, an
dem die Spuren der Schlägerei zu sehen waren. Nun begann er zu
rennen, um früher als der Vater heimzukommen. In Schweiß
gebadet betrat er die Küche, legte das Ohr an das Schloß der
Zimmertür und horchte. Alles still, nur die Nähmaschine schnurrte,
die Mutter war allein. O, Gott sei Lob und Dank! Hastig trat er ein
und sprudelte die Geschichte seines jüngsten Erlebnisses heraus:
„Und jetzt flick mir den Rock, Mutter, flick mir den Rock!“
Das Abendessen wurde schweigend eingenommen. Eine dumpfe
Verstimmung herrschte im Hause. Pfanner schmollte noch immer mit
seiner Frau. Er hatte die Scheine über alle von ihr versetzten
Gegenstände an sich genommen, um sie nach und nach einzulösen.
Gott weiß, unter welchen Bitternissen. Jeder Gulden, den er ins
Versatzamt trug, war ein Raub am Sparkassenbuch seines Sohnes;
an diesem künftigen Vermögen, aus dem die Kosten der Rigorosen
und des Freiwilligenjahres bestritten werden sollten. Es gab
Augenblicke, in denen er sie haßte, die Schuld an dem Raube trug.
Ihn gutzumachen, lag nicht in ihrer Macht, in der seinen aber lag, sie
büßen und leiden zu machen. Tag für Tag wiederholte sich dieselbe
Tortur. Tag für Tag verlangte er die Hausrechnung zu sehen, ging
jeden einzelnen Posten durch, bemängelte jeden. Mit raffinierter
Kunst erniedrigte er die Mutter in Gegenwart des Kindes durch sein
zur Schau getragenes Mißtrauen.
„Wer einmal betrogen hat, gleichviel in welcher Absicht, betrügt
wieder! man muß sich vor ihm in acht nehmen.“
Gepeinigt sah Georg zu ihr hinüber und warf ihr hinter dem
Rücken des Vaters Küsse zu. Um seinetwillen wurde sie beschämt, er
war der unschuldige Urheber ihrer Qual. Und sie, alles erratend, was
in ihm vorging, bezwang sich, bemühte sich, gelassen und standhaft
zu bleiben bei den Kränkungen, die sie erfuhr. Der Mann hielt für
Unempfindlichkeit, was höchster Heldenmut war, und verschärfte die
Lauge in den Ausdrücken seiner Geringschätzung. Wie immer war es
auch heute gegangen und Agnes kaum noch imstande, ihre
Selbstbeherrschung zu bewahren, als ein heftiger Riß an der Glocke
sie erschreckte. Sie schrie auf; auch Georg erschrak. Es war etwas
so völlig Ungewohntes, daß um diese Zeit jemand Einlaß bei ihnen
begehrte.
„Nervös, wie die elektrisierten Frösch,“ brummte Pfanner. „Habt ihr
in eurem Leben noch nicht läuten gehört? Sieh nach, wer's ist,“
befahl er der Frau.
Sie zündete rasch eine Kerze an und eilte in die Küche. Schon
wurde ein zweites Mal geschellt, noch ungeduldiger, noch heftiger
als früher. Als Agnes öffnete, stand ein großer, breitschultriger, fein
gekleideter Mann da und fragte:
„Ist Herr Offizial Pfanner zu Hause?“
Wer konnte das sein? Vielleicht ein Vorgesetzter, der Herr
Inspektor oder gar der Herr Oberinspektor?
„Ja, er ist zu Hause,“ sagte sie, „belieben einzutreten.“
Ohne Gruß ging er an ihr vorbei; er hielt sie offenbar für die Magd,
und ihr war der Irrtum recht. Sie hätte in ihrem grauen,
ausgewaschenen Percailkleide, in ihren geflickten Schuhen einem
Vorgesetzten gegenüber nicht für die Frau eines k. k. Beamten
gelten mögen. Höflich stieß sie die Zimmertür vor dem Fremden auf,
trat in die Küche zurück und hörte nur noch ihren Mann in durchaus
nicht respektvollem Tone sagen:
„Herr Obernberger? Was verschafft mir das Vergnügen?“
Obernberger schloß die Tür hinter sich, die Magd sollte das
Gespräch zwischen ihm und Pfanner nicht mit anhören.
„Vergnügen werden Sie von meinem Besuch nicht haben,“
erwiderte er in erregtem Tone, „ich komme, um mich zu beklagen.“
Hoho! Das konnte unangenehm werden. Pfanner hatte ein böses
Gewissen. War eine der wegwerfenden Reden, die er über
Obernberger zu führen pflegte, dem „Schlosser“ hinterbracht
worden? Vielleicht auch einem der Vorgesetzten, bei denen der
Meister in hohem Ansehen stand? Verfluchte Geschichte! Pfanner
verbarg seine Bestürzung hinter einem besonders borstigen Wesen:
„Nur heraus mit der Sprache, genieren Sie sich nicht. Ich kann was
vertragen,“ sagte er.
Georg war aufgesprungen und hatte einen Sessel herbeigeholt.
Obernberger nahm Platz. Er betrachtete den Knaben, der mit
gesenkten Augen und krampfhaft verschlungenen Fingern vor ihm
stehen blieb, streng und prüfend:
„Herr Obernberger! Herr Obernberger!“ sprach Georg leise und
flehentlich.
O, wenn er früher an Herrn Obernberger gedacht hätte, er würde
seinen Sohn nicht geprügelt haben. Herr Obernberger war immer so
gütig mit ihm, wenn er ihn traf, und neulich, als er im Wagen
gekommen war, den Pepi aus der Schule abzuholen, hatte er Georg
eingeladen, mitzufahren. Eine Seligkeit wäre es gewesen, der
Einladung zu folgen, aber er wagte es nicht. Der Vater hätte gewiß
gesagt: „Hast vergessen, daß du keine Gnaden annehmen sollst?“
Je länger Obernberger seine Augen auf Georg ruhen ließ, je milder
wurde ihr Ausdruck, und jetzt redete er ihn an: „Wissen Sie, daß ich
schon auf dem Wege zum Herrn Direktor war, um mich über Sie zu
beklagen? Ich mag Ihnen aber doch Ihre gute Note in Sitten nicht
verderben und will mich mit einer häuslichen Züchtigung begnügen,
die Ihnen Ihr Vater sicher erteilen wird, wenn er hört, was
vorgefallen ist. Herr Offizial,“ wendete er sich an Pfanner, „Georg hat
heute nach der Schule meinen Sohn angefallen und ihn gewürgt,
und andre haben sich hineingemischt, und mein Pepi ist mir nach
Hause gekommen, ganz zerrissen, und das rechte Auge so blau und
geschwollen, daß er ein paar Tage hindurch weder lesen noch
schreiben kann. Und das ist geschehen ohne den geringsten Grund.“
„Ohne den geringsten Grund?“ wiederholte Pfanner, hob sich halb
von seinem Sitz, und es war, als ob er auf den Sohn losspringen
wollte.
„Nicht ohne Grund,“ hauchte Georg mehr als er sprach. „Er hat
mir gesagt, daß ich ein Büffler bin. Büffeln kommt von Büffel, und
Büffel gehören zu der Gruppe der Rinder, hat er gesagt.“
Pfanner schwieg und saß wieder gerade auf seinem Sessel.
Obernberger war betroffen.
„Ist das wahr?“ fragte er, und Georg beteuerte:
„Es ist wahr.“
„Hinaus!“ rief Pfanner ihm plötzlich zu und wies mit
ausgestrecktem Arm nach der Küchentür.
Draußen stand die Mutter neben dem Herde und zitterte an allen
Gliedern und fragte sich, was für ein neues Unheil über ihren Georg
hereingebrochen sein möchte. Er lief auf sie zu, war bleich wie
Wachs, und grünliche Schatten zogen sich längs der Nase zu den
Mundwinkeln herab: „Mutter, Mutter!“ preßte er hervor, „was wird
jetzt mit mir geschehen?“

In der Stube jedoch begab sich das Unerhörte. Pfanner


entschuldigte seinen Sohn. Der Junge war schüchtern von Natur und
nur zu sanft für einen Buben. Wenn er einmal losgeschlagen hatte,
mußte er arg provoziert worden sein. Er sei auch absolut wahrhaft,
versicherte der Vater, der ihn noch nie auf einer Lüge ertappt hatte.
„Können Sie das von Ihrem Pepi auch sagen?“ fragte Pfanner und
setzte die gewisse, militärische Miene auf, die er sich angeeignet
hatte, als er einst, nach wenigen Monaten seiner Dienstzeit, zum
Korporal befördert worden war.
Der gutmütige Obernberger stand immer noch unter dem
Eindruck, den die Todesangst auf dem Gesichte Georgs auf ihn
gemacht hatte. Der große, breite Mensch schmolz in der Nähe des
kleinen, hitzigen Pfanner ordentlich zusammen. Ein gewaltiger
Schneemann in der Nähe eines Häufleins glühender Kohlen. Er hatte
keine Ursache, sich auf die Wahrheitsliebe seines Pepi zu verlassen,
und weil er das nicht eingestehen wollte, schwieg er.
„Fragen Sie Ihren Pepi aufs Gewissen, ob mein Sohn ihn wirklich
ohne Grund geschlagen hat,“ sprach Pfanner. „Aug in Aug mit dem
Buben, in unsrer Gegenwart soll er es ihm wiederholen. Tut er das,
dann lade ich Sie zu einer Exekution ein, wie sie bei uns noch nicht
stattgefunden hat, obwohl i c h bei meinem Buben die Prügel nicht
spare.“
Bei dieser Abmachung blieb es. Herr Obernberger, der als Richter
gekommen war, verließ die Wohnung des Offizials mit dem Gefühl,
eine Niederlage erlitten zu haben. Er achtete nicht auf die zwei, die
sich tief verneigten, als er die Küche durchschritt. Georg lief ihm
voran, öffnete mit demütiger Beflissenheit die Tür und murmelte:
„Verzeihen Sie mir, Herr Obernberger, verzeihen Sie mir,“ so leise,
mit so von Scheu und Tränen erstickter Stimme, daß der in
unangenehme Gedanken versunkene Fabriksherr nichts davon hörte.
Als Agnes und Georg das Zimmer wieder betraten, hatte Pfanner
einen großen, mit Zahlen bedeckten Bogen vor sich liegen, den er
mit äußerster Aufmerksamkeit durchsah. Georg holte seine Hefte
herbei und machte sich an seine Arbeit. Eine halbe Stunde verging,
ehe der Vater seinen Sohn ansprach, und dann – o Wunder! geschah
es nicht einmal in unfreundlicher Weise. Er überzeugte sich, daß
Georg beinahe fertig war mit seinen Aufgaben:
„Bist du aus Geschichte schon aufgerufen worden?“ fragte er.
„Noch nicht.“
„Merkwürdig. So spät?“
„Vielleicht morgen. Wir haben morgen Geschichte.“
„Nun, da kriegst du doch eine Vorzugsklasse?“
„Ich weiß nicht, vielleicht.“
„Du!“ schrie der Vater ihn an. „Weißt du, was das heißt, wenn du
keine Vorzugsklasse kriegst? Weißt du, was ein ‚Genügend‘ dich
kostet?“
„Ich weiß es,“ erwiderte Georg tonlos.
„Den Vorzugsschüler kostet's dich, fauler Bub!“
„Ich bin nicht faul, Vater.“
Der Vater hob namenlos erstaunt den Kopf. Sein friedfertiger
Junge war heute der Held einer Prügelei gewesen, und jetzt vermaß
er sich, ihm zu widersprechen. Was war vorgegangen? War in dem
Jungen der Mann erwacht? Sollte er am Ende noch so schneidig
werden, wie er sich ihn immer gewünscht?
Frau Agnes hatte ihre Hand auf den Arm des Sohnes gelegt, als er
dem Vater widersprochen: „Um Gottes willen, Schorsch!“
„Still,“ herrschte Pfanner sie an, „laß ihn reden. Ich bin nicht faul,
behauptet er. Also red, 's ist erlaubt, 's ist befohlen,“ drang er in ihn.
„Ich lern den ganzen Tag,“ sagte Georg. „Ich kann nicht mehr
lernen als ich lern, ich weiß nicht, was ich anfangen soll, damit du
zufrieden bist.“ Die Tollkühnheit der Verzweiflung kam über ihn, und
er wagte hinzuzusetzen: „Andre Eltern sind schon zufrieden, wenn
ihre Kinder ‚Genügend‘ bekommen, und ich soll lauter ‚Vorzüglich‘
und ‚Lobenswert‘ haben ... Und ich soll mich schinden ... Und ich ...“
Er konnte nicht weiter reden, rang die Hände, schlug mit der Stirn
auf den Tisch und wand sich in einem Schmerze, über den der Vater
selbst erschrak. Zum erstenmal im Leben fühlte er sich ratlos dem
Kinde gegenüber.
„Ich hab schon ein ‚Genügend‘ in Griechisch!“ schrie Georg in
pfeifenden, gequetschten Tönen. „Wenn ich noch ein ‚Genügend‘
bekomme, bin ich kein Vorzugsschüler mehr. Und ich bekomm gewiß
noch ein ‚Genügend‘ ...“
Das war zu viel. Die Worte machten der Langmut Pfanners ein
Ende. Alles in ihm, das ein bißchen weich zu werden begonnen
hatte, erstarrte wieder:
Kein Vorzugsschüler mehr! Dieser Bub, der die Fähigkeit besaß,
einen Platz unter den Ausgezeichneten zu behaupten, wollte durch
die Schule kriechen mit dem großen Heer der Mittelmäßigen? Pfui
über den Buben!
„Du bleibst Vorzugsschüler, oder ich geb dich zu einem Schuster in
die Lehr.“
„Tu's, Vater, tu's! Aber warum grad zu einem Schuster!“ erwiderte
Georg außer sich. „Du kannst mich auch zu Herrn Obernberger
geben, und ich werd ein Kunstschlosser ... Oder auch mit Musik
kann ich mein Brot verdienen ...“
„Georg, Georg, um Gottes willen!“ wiederholte die Mutter. Sie sah
ihren Mann fahl werden vor Wut, sah seine Fäuste sich ballen:
„Musik? gut, gut! Ich kauf dir einen Leierkasten, kannst in den
Häusern orgeln und auf die Kreuzer warten, die sie dir aus den
Fenstern werfen.“
Georg preßte das Kinn auf die Brust und starrte zu Boden.
Pfanner sprang auf und führte einen schweren Schlag auf den
Nacken des Kindes: „Kein Wort mehr! Und – das merke, komm mir
nicht noch einmal mit einer schlechten Note nach Hause. Untersteh
dich nicht!“
„Nein, nein,“ murmelte Georg. Er war jetzt ganz furchtlos. Um so
besser, wenn er nicht mehr nach Hause zu kommen braucht. Der
Vater wird sich nicht mehr über ihn ärgern, und die Mutter nicht
mehr quälen um seinetwillen. Wäre er doch nicht auf die Welt
gekommen ... – oder wäre er schon draußen – wäre er tot!
Am nächsten Morgen war der Vater von einer furchtbar dräuenden
Schweigsamkeit. Die dunkeln Ringe unter seinen geröteten Augen,
bei ihm das sicherste Zeichen einer schlaflos durchwachten Nacht,
gaben ihm das Aussehen eines Kranken. Er frühstückte hastig, nahm
seine Schriften unter den Arm, setzte den Hut auf und verließ das
Zimmer, ohne den Gruß seiner Frau und seines Sohnes zu erwidern.
Man hörte ihn die Küchentür zuschlagen, daß sie dröhnte.
Georg ordnete die Hefte und Bücher in seiner Schultasche, war
fertig, nahm Stück auf Stück wieder heraus, ordnete alles von
neuem, langsam und bedächtig. Die Mutter mahnte zur Eile. Er ließ
plötzlich alles liegen und stehen und warf sich ihr in die Arme, und
sie drückte ihn an ihr Herz. Sie sprachen nicht, es kam keine Anklage
über ihre Lippen, aber glühend brannte sie in ihren Herzen. Wie
glücklich könnten sie sein, sie zwei, wie glückselig, wenn der Ehrgeiz
des Vaters nicht wäre, der blinde, törichte, der vom Apfelbäumchen,
das ihm Gott in seinen Garten gepflanzt, die Triebkraft der Eiche
verlangte.
Dreimal schon hatte Georg Lebewohl gesagt und brachte sich
noch immer nicht fort.
„Du kommst zu spät, Schorschi,“ sagte Frau Agnes. „Lauf jetzt,
lauf! Und sei nicht so traurig,“ fügte sie hinzu und strich ihm über
die Wangen.
„Du bist selbst traurig,“ antwortete er.
„Ach – das vergeht, bei der Arbeit vergeht's.“
„Also adieu,“ sagte er und schritt resolut der Tür zu, und über die
Treppe hinab bis zum ersten Stockwerk. Dort blieb er stehen, besann
sich, kehrte plötzlich um und stürmte in raschen Sätzen wieder
zurück, und wie er oben ankam, sah er die Mutter vor der
Wohnungstür stehen, auf derselben Stelle, bis zu der sie ihn
begleitet hatte.
„Was gibt's?“ fragte sie wie aus dem Schlaf auffahrend, warf den
Kopf zurück und bemühte sich, eine strenge Miene anzunehmen.
„Hast was vergessen?“
„Ich hab dir ja nicht ordentlich Adieu gesagt,“ und er fiel ihr um
den Hals und küßte sie mit stürmischer Zärtlichkeit.
In der Schule kam er zu spät. Der erste Vortrag hatte schon vor
einer Viertelstunde begonnen, als er eintrat und sich auf seinen Platz
setzte.
„Wo steckst denn?“ raunte der Nachbar ihm zu. „Du bist
aufgerufen worden und warst nicht da.“
„Unglück, Unglück,“ murmelte Georg und gab sich alle erdenkliche
Mühe, aufmerksam zuzuhören. In seinem Kopfe ging es sonderbar
zu. Es summte und hämmerte darin, und der Stimme, die vom
Katheder zu ihm herübertönte – sonst eine laute, kraftvolle Stimme
–, fehlte der Klang. Die Worte, die sie sprach, waren nicht artikuliert,
flossen ineinander wie Wellen ... Noch etwas Sonderbares! der breite
Saal schien sich zu verlängern ins Unglaubliche. Es war kein Saal
mehr, es war ein langer Gang, von merkwürdig kaltem, weißem Licht
erfüllt, und ganz weit am Ende stand ein schwarzer Strich auf einem
Piedestal. Georg mußte mit Gewalt alle seine Denkkraft zusammen
nehmen, um sich klar zu machen: das ist der Herr Professor, der
einen Vortrag hält.
Er schloß die Augen, lehnte sich zurück und dachte: Ich werde
heute nicht lernen können. Nach einer Weile aber wurde es besser,
er vermochte sich aus dem unheimlich traumhaften Zustand, in den
er geraten war, heraus zu reißen. Der zweite Vortrag hatte
begonnen. Der jetzt sprach, war ein sehr beliebter, von der ganzen
Schule verehrter Lehrer, der Professor der Geschichte. Er hatte einen
sonst kaum mittelmäßigen Schüler aufgerufen, und der bestand mit
Ehren. Georg folgte. Ach! wenn er auch so viel Glück hätte wie sein
Vorgänger. Es schien beinahe. Der Professor prüfte aus dem
unlängst von Georg Wiederholten und sagte:
„Gut, bis auf zwei Jahreszahlen. Sie bekommen ‚Lobenswert‘. Ich
möchte Ihnen aber gern ‚Vorzüglich‘ geben können und stelle
deshalb noch einige Fragen. Nennen Sie mir alle deutschen Kaiser
bis zu Rudolf dem Ersten.“
Das war keine sehr schwere Frage. Voll Zuversicht begann er sie
zu beantworten und gelangte glorreich bis zu Otto III. Da verriet ihn
sein Gedächtnis – er ließ den gelehrten und frommen Kaiser ein
hohes Alter erreichen und Heinrich II. den ersten Salier sein.
Der Professor zuckte bedauernd die Achseln und unterbrach ihn:
„Das geht nicht gut. – Etwas andres! Erzählen Sie mir die Geschichte
von Konradin.“
O – die wußte er! die hatte er seiner Mutter erzählt; so rührend,
daß sie dabei weinen mußte. Konradin war ja – nun ja – war ja
König Enzio ... Oder nein, richtig – Enzio war Konradin ...
Ein kaum unterdrücktes boshaftes Kichern erhob sich, der Pepi
lachte ihn aus. Die Augen des Professors hefteten sich fest auf ihn.
Er verstand, daß diese guten, wohlwollenden Augen ganz besorgt
fragten: „Sind Sie bei Trost?“
Er hätte schreien mögen: „Nein! ganz verwirrt und konfus bin ich!“
„Sie tun mir leid,“ sprach der Professor, „aber – sagen Sie selbst –
welche Klasse haben Sie verdient?“
Georg flüsterte etwas völlig Unverständliches. Dem Lehrer schien,
es sei ein Dank gewesen. Der Junge wußte heute nichts, erriet aber
viel, erriet das innige Mitleid, das er seinem Lehrer einflößte.
Ehe der dritte Vortrag begann, verließ er die Schule und ging
langsam die Straße hinab. Es war ein Frühlingstag mit sommerlichem
Sonnenschein, der Himmel wolkenlos, die Luft noch frei von Staub
und Dunst. Georg schritt mit weit aufgerissenen, verglasten Augen
zwischen den Menschen dahin, die sich in der Hauptverkehrsstraße
der Vorstadt drängten. Einem oder dem andern fiel auf, wie
sonderbar „verloren“ er aussah. Keiner hatte Lust und Zeit, ihn zu
fragen, was ihm sei. Ein Tischlerjunge nur, der einen Handwagen
schleppte, und an den er angestoßen war, rief ihm zu:
„Hüo! wo hast dein Schädel? Anbaut mit samt der Mitzen?“
Unwillkürlich griff Georg nach seinem Kopfe. Er war barhaupt,
hatte seine Mütze in der Schule gelassen, und auch seine
Lernsachen. Daran lag aber nichts. Ihn würde niemand nach ihnen
fragen. Er konnte ja nicht mehr heim. „Komm mir nicht nach Hause
mit einer schlechten Note!“ Diese Worte dröhnten unablässig an sein
Ohr. Jetzt mußte er sie bekommen, die schlechte Note, die erste,
wirklich schlechte. Was würde der Vater jetzt mit ihm tun? Und wie
würde die Mutter sich kränken ... Nein, nein, Vater und Mutter, er
wagt es nicht, er kommt nicht mehr zurück, er geht, wohin schon
mancher unglückliche Schüler gegangen ist: in die Donau. Und
dieser eine Gedanke, je länger er ihn vor sich sah, als das
Unabwendbare, Einzige, je mehr befreundete er sich mit ihm. Dieser
Gedanke mit dem dunklen Kerne hatte eine blendende Atmosphäre
und fing an, eine große Helligkeit zu verbreiten. Er gestaltete sich
jetzt so: „Ich m u ß in die Donau, ich will aber auch, und gern. Wie
gut ist es, tot zu sein, nicht mehr hören müssen: Lern! Wie gut
auch, wenn es keinen Zwiespalt mehr zwischen den Eltern gibt. Aber
du begehst einen Selbstmord,“ fuhr es ihm durch den Sinn, „und ein
Selbstmord ist eine Todsünde.“ Ihn schauderte. „Lieber Gott!
Allgütiger!“ stöhnte er und blickte flehend zum Himmel empor.
„Rechne mir meinen Tod nicht als Sünde an! Ich will keine Sünde
begehen, ich will sterben für den Frieden meiner Eltern. Mein Tod ist
ein Opfertod.“
Ein Opfertod!
An dieses Wort klammerte er sich; es brachte ihm Trost. Er
verwandelte die Tat der Verzweiflung in eine Heldentat und
schwerste Schuld in ein Märtyrertum. Es ging auf vor dem armen,
irrenden, suchenden Kinde wie ein Stern in der Nacht. Keine
Erwägung, keine Überlegung, kein Zweifel mehr, nicht die geringste
Fähigkeit, sich etwas andres vorzustellen, nur die rasende,
unbezwingliche Sehnsucht, Erlösung zu erfahren und Erlösung zu
bringen.
Er war am Ende der Straße angelangt, bog in die Seitengasse ein,
die auf den Kai mündete. Bleierne Müdigkeit lag ihm in den Gliedern,
sein Kopf brannte und schmerzte bis zur Bewußtlosigkeit. Die Donau,
die ist ein kühles, weiches Bett, da findet man Ruhe und Labung.
Nur sie erreichen, nur bis zu ihr hinkommen! Eine dumpfe Angst:
„sie mißgönnen mir die Erlösung, sind hinter mir, verfolgen mich,“
jagte ihn vorwärts. Er begann zu laufen, und dabei schien ihm, daß
er immer auf demselben Fleck bliebe. Das war fürchterlich, noch
einmal einen so argen Kampf mit dem Unüberwindlichen kämpfen zu
müssen.
„Wohin? Was sind Sie so eilig?“ sprach eine wohlbekannte Stimme
ihn an. Der Hausierer stand vor ihm.
„Du?“ sagte er, „du Salomon?“
Ein wenig Zeit nahm er sich zum Abschied von dem Armen. Auch
der war elend, dem es Seligkeit gewesen wäre, in der Schule zu
sitzen, aus der Georg entflohen war, und der auf und ab wandeln
mußte vom frühen Morgen bis in die späte Nacht in Staub und
Sonnenbrand, und sah so krank aus, und seine schmächtige Gestalt
war schon ganz schief vom Tragen des schweren Warenkastens. Ja,
ja, wem zu Schweres auferlegt wird, der verkrüppelt. Armer
Salomon, den der Wachmann aufscheucht und einzuführen droht,
wenn er ganz erschöpft einige Augenblicke auf einer Bank ausruhen
möchte. Fort, fort auf müden Füßen in den ausgetretenen,
geplatzten Stiefeln ... Georgs Blick glitt über sie hinweg, und
plötzlich beugte er sich, zog rasch seine neuen Halbschuhe aus und
legte sie auf den Warenkasten.
„Nimm sie, ich brauche sie nicht mehr,“ sprach er und – lachte. Ja,
wahrhaftig, Salomon schwor später darauf, daß er gelacht habe, und
wie unaussprechlich schmerzvoll dieses Lachen geklungen, kam ihm
erst später zum Bewußtsein, nachdem alles vorüber war. Zuerst in
seiner freudigen Verblüffung hatte er nur Augen für die schönen,
guten Schuhe, die ihm wie aus dem Füllhorn des Glückes zugefallen
waren. Als er sich besann, daß Georg seine Schuhe gar nicht
verschenken dürfe, und wohl nur einen Spaß mit ihm gemacht habe
und er sich umsah und rief: „Junger Herr! junger Herr!“ – drang
schon lautes, vielstimmiges Geschrei an sein Ohr: „Im Wasser!“ –
„Hineingesprungen!“ – „Hilfe! Hilfe!“ Von allen Seiten stürzten sie
herbei, rannten, krochen die steile Böschung hinab, standen mit
vorgestreckten Hälsen, Entsetzen oder stumpfsinnige oder
abscheuliche Neugier in den Gesichtern, und deuteten: „Da! dort!
Siehst ihn?“
Anstalten zur Rettung wurden getroffen – vergebliche. Eine
Stromschnelle hatte den schwimmenden Körper erfaßt und
häuptlings an einen Brückenpfeiler geschleudert.
Mit gellenden Wehrufen drängte sich Salomon durch die Menge
zum Ufer hin. Die Schuhe hatte er von sich geworfen, streute seine
Waren im Laufe achtlos aus ... Gott! Gott! Ins Wasser gesprungen –
in den Tod gegangen, der, den er bewundert hatte und beneidet,
und der immer so gut gegen ihn gewesen war.

Pfanner hatte einen schweren Entschluß gefaßt und ausgeführt. Er


war zum Direktor des Gymnasiums gegangen, um Georg seiner
Nachsicht zu empfehlen. Vor wenigen Tagen noch würde er einen
solchen Schritt für unmöglich gehalten und geglaubt haben, sich und
Georg durch ihn zu erniedrigen.
Mit so viel Wärme und Verbindlichkeit, als ihm irgend zu Gebote
stand, sprach er die Bitte aus, seinen Sohn nachsichtig zu
klassifizieren, wenn der Bursche auch in letzter Zeit etwas
nachgelassen habe im Fleiße. Sein Vater bürgte dafür, daß es von
nun an besser werden sollte.
„Nachgelassen im Fleiße?“ Das war dem Direktor neu. So viel er
wußte, hatte noch keiner der Professoren sich über Georgs Mangel
an Fleiß beklagt. „Ich wäre froh“, sagte er, „wenn ich allen Eltern so
Gutes über ihre Söhne sagen könnte, wie Ihnen über Georg. Er ist
bei sämtlichen Lehrern vortrefflich angeschrieben, sehr brav und
auch durchaus nicht unbegabt“ ...
„O, das glaub ich!“ warf Pfanner hochfahrend ein.
„Durchaus nicht unbegabt,“ wiederholte der Direktor kühl, „aber
auch nicht ungewöhnlich begabt. Ich fürchte, daß Sie zu viel von ihm
verlangen, ihm eine größere Leistungsfähigkeit zutrauen, als er
besitzt. Wenn Sie ihn zwingen, seine Kräfte zu überspannen,
ruinieren Sie ihn.“
Der Offizial kam tief niedergeschlagen ins Bureau. So verlangte er
also zu viel von seinem Buben, so ruinierte er ihn, so sollte Georg
nur mittelmäßig begabt sein? Er glaubte es nicht. Diese Schulleute
irren so oft. Wie viele, von denen ihre Lehrer nichts gehalten, sind
große Männer geworden. Er ging an seine Arbeit, vergrub sich in sie,
suchte Rettung in ihr vor dem schweren Drucke, der ihm auf dem
Herzen lastete.
Gegen Mittag meldete ihm der Bureaudiener, es sei jemand da,
der ihn sprechen wolle. Auf dem Gange erwartete ihn Frau Walcher
in einem Zustand furchtbarer Zerstörtheit. Etwas Entsetzliches sei
geschehen, stotterte sie, das Ärgste, das man sich denken könne. Er
solle nur gleich mit ihr kommen.
„Was ist das Ärgste?“ fuhr er sie an. „Was ist's mit meinem
Buben?“
Ihre Antwort war eine Gebärde der Verzweiflung.

Dem Liebling des Gymnasiums wurde ein feierliches


Leichenbegängnis bereitet. Alle Professoren, alle Schulkameraden
beteiligten sich daran. Meister Obernberger folgte dem Zuge,
weinend wie ein Kind, und sein Pepi hatte heute allen Hochmut
abgetan.
Der Vater schritt in guter Haltung hinter dem Sarge. Jedes Wort,
das am Grabe zum Preise seines Sohnes gesprochen wurde, schien
ihm wohl zu tun, während die Mutter immer tiefer in sich
zusammensank.
„Am besten für sie wär's,“ sagte schwerbekümmert Frau Walcher
zu ihrem Manne, „wenn man sie gleich mitbegraben könnt.“
Die zwei Ehepaare traten die Rückfahrt im selben Wagen an.
Pfanner und seine Frau wechselten nicht eine Silbe. Einer wich scheu
dem Blick des andern aus. Daheim angelangt, gab Agnes den
dringenden Bitten der Freundin, zuerst bei ihr einzutreten, nach.
„Da hat sie doch ein paar Stunden Frieden,“ dachte die Getreue.
Als der Abend kam und die gewohnte Pflicht sie rief, ging Agnes
mechanisch daran, das Abendbrot zu bereiten. Sie betrat das
Zimmer, um die Lampe anzuzünden. Aber Pfanner hatte das schon
selbst getan. Die Lampe brannte auf dem Tische, und dort lagen die
Bücher und die Mütze, die der Schuldiener zurückgebracht hatte. Vor
sich aufgeschlagen hatte Pfanner ein dünnes Büchlein – das
Vermögen des Kindes, das guldenweise zusammen gesparte. Und in
der gebrochenen Gestalt, die da saß und die Gegenstände alle
betrachtete, drückte eine herzzerreißende Trostlosigkeit sich aus.
Was ging jetzt vor in dieser Seele!
Agnes kam leise heran.
Die Frau, die er zermalmt und zertreten und zu einer dienenden
Maschine herabgewürdigt hatte, fühlte sich in diesem Augenblick als
die Größere und Stärkere und, im Vergleiche zu ihm – die Glückliche.
Sie durfte ihres Kindes ohne Selbstvorwurf gedenken, von ihr hatte
es mit zärtlicher Liebe Abschied genommen.
„Pfanner,“ sprach sie.
Er fuhr auf und starrte sie an mit Entsetzen. Wollte sie
Rechenschaft von ihm fordern? Seine Lippen zuckten und zitterten,
er brachte keinen Laut hervor. Etwas Greisenhaftes lag in seinen
entstellten Zügen.
Da wich der Haß, da schwieg jeder Vorwurf. Sie näherte sich
langsam und sagte:
„Du hast ja nur sein Bestes gewollt.“
Überrascht in demütiger Dankbarkeit nahm er ihre beiden Hände,
legte sein Gesicht hinein und schluchzte.
Er laßt die Hand küssen.

„So reden Sie denn in Gottes Namen“, sprach die Gräfin, „ich
werde Ihnen zuhören; glauben aber nicht ein Wort.“
Der Graf lehnte sich behaglich zurück in seinem großen
Lehnsessel: „Und warum nicht?“ fragte er.
Sie zuckte leise mit den Achseln: „Vermutlich erfinden Sie nicht
überzeugend genug.“
„Ich erfinde gar nicht, ich erinnere mich. Das Gedächtnis ist meine
Muße.“
„Eine einseitige, wohldienerische Muße! Sie erinnert sich nur der
Dinge, die Ihnen in den Kram passen. Und doch gibt es auf Erden
noch manches Interessante und Schöne außer dem – Nihilismus.“
Sie hatte ihre Häkelnadel erhoben und das letzte Wort wie einen
Schuß gegen ihren alten Verehrer abgefeuert.
Er vernahm es ohne Zucken, strich behaglich seinen weißen Bart
und sah die Gräfin beinahe dankbar aus seinen klugen Augen an.
„Ich wollte Ihnen etwas von meiner Großmutter erzählen,“ sprach er.
„Auf dem Wege hierher, mitten im Walde, ist es mir eingefallen.“
Die Gräfin beugte den Kopf über ihre Arbeit und murmelte: „Wird
eine Räubergeschichte sein.“
„O, nichts weniger! So friedlich wie das Wesen, durch dessen
Anblick jene Erinnerung in mir wachgerufen wurde, Mischka IV.
nämlich, ein Urenkel des ersten Mischka, der meiner Großmutter
Anlaß zu einer kleinen Übereilung gab, die ihr später leid getan
haben soll,“ sagte der Graf mit etwas affektierter Nachlässigkeit, und
fuhr dann wieder eifrig fort: „Ein sauberer Heger, mein Mischka, das
muß man ihm lassen! er kriegte aber auch keinen geringen
Schrecken, als ich ihm unvermutet in den Weg trat – hatte ihn
vorher schon eine Weile beobachtet ... Wie ein Käfersammler schlich
er herum, die Augen auf den Boden geheftet, und was hatte er im
Laufe seines Gewehres stecken? Denken Sie: – ein Büschel
Erdbeeren!“
„Sehr hübsch!“ versetzte die Gräfin. „Machen Sie sich darauf
gefaßt – in Bälde wandern Sie zu mir herüber durch die Steppe, weil
man Ihnen den Wald fortgetragen haben wird.“
„Der Mischka wenigstens verhindert's nicht.“
„Und Sie sehen zu?“
„Und ich sehe zu. Ja, ja, es ist schrecklich. Die Schwäche liegt mir
im Blut – von meinen Vorfahren her.“ Er seufzte ironisch und sah die
Gräfin mit einer gewissen Tücke von der Seite an.
Sie verschluckte ihre Ungeduld, zwang sich, zu lächeln und suchte
ihrer Stimme einen möglichst gleichgültigen Ton zu geben, indem sie
sprach: „Wie wär's, wenn Sie noch eine Tasse Tee trinken und die
Schatten Ihrer Ahnen heute einmal unbeschworen lassen würden?
Ich hätte mit Ihnen vor meiner Abreise noch etwas zu besprechen.“
„Ihren Prozeß mit der Gemeinde? – Sie werden ihn gewinnen.“
„Weil ich recht habe.“
„Weil Sie vollkommen recht haben.“
„Machen Sie das den Bauern begreiflich. Raten Sie ihnen, die
Klage zurückzuziehen.“
„Das tun sie nicht.“
„Verbluten sich lieber, tragen lieber den letzten Gulden zum
Advokaten. Und zu welchem Advokaten, guter Gott!... ein ruchloser
Rabulist. Dem glauben sie, mir nicht, und wie mir scheint, Ihnen
auch nicht, trotz all Ihrer Popularitätshascherei!“
Die Gräfin richtete die hohe Gestalt empor und holte tief Atem.
„Gestehen Sie, daß es für diese Leute, die so töricht vertrauen und
mißtrauen, besser wäre, wenn ihnen die Wahl ihrer Ratgeber nicht
frei stände.“
„Besser wär's natürlich! Ein bestellter Ratgeber, und – auch
bestellt – der Glaube an ihn.“
„Torheit!“ zürnte die Gräfin.
„Wie so? Sie meinen vielleicht, der Glaube lasse sich nicht
bestellen?... Ich sage Ihnen, wenn ich vor vierzig Jahren meinem
Diener eine Anweisung auf ein Dutzend Stockprügel gab und dann
den Rat, aufs Amt zu gehen, um sie einzukassieren, nicht einmal im
Rausch wäre es ihm eingefallen, daß er etwas Besseres tun könnte,
als diesen meinen Rat befolgen.“
„Ach, Ihre alten Schnurren! – Und ich, die gehofft hatte, Sie heute
ausnahmsweise zu einem vernünftigen Gespräch zu bringen!“
Der alte Herr ergötzte sich eine Weile an ihrem Ärger und sprach
dann: „Verzeihen Sie, liebe Freundin. Ich bekenne, Unsinn
geschwatzt zu haben. Nein, der Glaube läßt sich nicht bestellen, aber
leider der Gehorsam ohne Glauben. Das eben war das Unglück des
armen Mischka und so mancher andrer, und deshalb bestehen
heutzutage die Leute darauf, wenigstens auf ihre eigne Fasson ins
Elend zu kommen.“
Die Gräfin erhob ihre nachtschwarzen, noch immer schönen Augen
gegen den Himmel, bevor sie dieselben wieder auf ihre Arbeit senkte
und mit einem Seufzer der Resignation sagte: „Die Geschichte
Mischkas also!“
„Ich will sie so kurz machen als möglich,“ versetzte der Graf, „und
mit dem Augenblick beginnen, in dem meine Großmutter zum
erstenmal auf ihn aufmerksam wurde. Ein hübscher Bursche muß er
gewesen sein; ich besinne mich eines Bildes von ihm, das ein
Künstler, der sich einst im Schlosse aufhielt, gezeichnet hatte. Zu
meinem Bedauern fand ich es nicht im Nachlaß meines Vaters und
weiß doch, daß er es lange aufbewahrt hat, zum Andenken an die
Zeiten, in denen wir noch das jus gladii ausübten.“
„O Gott!“ unterbrach ihn die Gräfin, „spielt das jus gladii eine Rolle
in Ihrer Geschichte?“
Der Erzähler machte eine Bewegung der höflichen Abwehr und
fuhr fort: „Es war bei einem Erntefest und Mischka einer der
Kranzträger, und er überreichte den seinen schweigend, aber nicht
mit gesenkten Augen, sah vielmehr die hohe Gebieterin ernsthaft
und unbefangen an, während ein Aufseher im Namen der
Feldarbeiter die übliche Ansprache herunterleierte.
„Meine Großmutter erkundigte sich nach dem Jungen und hörte,
er sei ein Häuslersohn, zwanzig Jahre alt, ziemlich brav, ziemlich
fleißig und so still, daß er als Kind für stumm gegolten hatte, für
dummlich galt er noch jetzt. – Warum? wollte die Herrin wissen;
warum galt er für dummlich?... Die befragten Dorfweisen senkten
die Köpfe, blinzelten einander verstohlen zu und mehr als: ‚So, – ja
eben so‘, und: – ‚je nun, wie's schon ist‘, war aus ihnen nicht
herauszubringen.
„Nun hatte meine Großmutter einen Kammerdiener, eine wahre
Perle von einem Menschen. Wenn er mit einem Vornehmen sprach,
verklärte sich sein Gesicht dergestalt vor Freude, daß er beinahe
leuchtete. Den schickte meine Großmutter andern Tages zu den
Eltern Mischkas mit der Botschaft, ihr Sohn sei vom Feldarbeiter zum
Gartenarbeiter avanciert und habe morgen den neuen Dienst
anzutreten.
„Der eifrigste von allen Dienern flog hin und her und stand bald
wieder vor seiner Gebieterin. ‚Nun,‘ fragte diese – ‚was sagen die
Alten?‘ Der Kammerdiener schob das rechte, auswärts gedrehte Bein
weit vor ...“
„Waren Sie dabei?“ fiel die Gräfin ihrem Gaste ins Wort.
„Bei dieser Referenz gerade nicht, aber bei späteren des edlen
Fritz,“ erwiderte der Graf, ohne sich irre machen zu lassen. „Er schob
das Bein vor, sank aus Ehrfurcht völlig in sich zusammen und
meldete, die Alten schwämmen in Tränen der Dankbarkeit.
„‚Und der Mischka?‘
„‚O, der‘ – lautete die devote Antwort, und nun rutschte das linke
Bein mit anmutigem Schwunge vor – ‚o der – der laßt die Hand
küssen.‘
„Daß es einer Tracht väterlicher Prügel bedurft hatte, um den
Burschen zu diesem Handkuß im Gedanken zu bewegen, verschwieg
Fritz. Die Darlegung der Gründe, die Mischka hatte, die Arbeit im
freien Felde der im Garten vorzuziehen, würde sich für Damenohren
nicht geschickt haben. – Genug, Mischka trat die neue Beschäftigung
an und versah sie schlecht und recht. ‚Wenn er fleißiger wäre,
könnt's nicht schaden,‘ sagte der Gärtner. Dieselbe Bemerkung
machte meine Großmutter, als sie einmal vom Balkon aus zusah, wie
die Wiese vor dem Schlosse gemäht wurde. Was ihr noch auffiel,
war, daß alle andern Mäher von Zeit zu Zeit einen Schluck aus einem
Fläschchen taten, das sie unter einem Haufen abgelegter Kleider
hervorzogen und wieder darin verbargen. Mischka war der einzige,
der diesen Quell der Labung verschmähend sich aus einem irdenen,
im Schatten des Gebüsches aufgestellten Krüglein erquickte. Meine
Großmutter rief den Kammerdiener. ‚Was haben die Mäher in der
Flasche?‘ fragte sie. – ‚Branntwein, hochgräfliche Gnaden.‘ – ‚Und
was hat Mischka in dem Krug?‘
„Fritz verdrehte die runden Augen, neigte den Kopf auf die Seite,
ganz wie unser alter Papagei, dem er ähnlich sah wie ein Bruder
dem andern, und antwortete schmelzenden Tones: ‚Mein Gott,
hochgräfliche Gnaden – Wasser!‘
„Meine Großmutter wurde sogleich von einer mitleidigen Regung
ergriffen und befahl, allen Gartenarbeitern nach vollbrachtem
Tagewerk Branntwein zu reichen. ‚Dem Mischka auch,‘ setzte sie
noch eigens hinzu.
„Diese Anordnung erregte Jubel. Daß Mischka keinen Branntwein
trinken wollte, war einer der Gründe, warum man ihn für dummlich
hielt. Jetzt freilich, nachdem die Einladung der Frau Gräfin an ihn
ergangen, war's aus mit Wollen und Nichtwollen. Als er in seiner
Einfalt sich zu wehren versuchte, ward er mores gelehrt, zur
höchsten Belustigung der Alten und der Jungen. Einige rissen ihn auf
den Boden nieder, ein handfester Bursche schob ihm einen Keil
zwischen die vor Grimm zusammengebissenen Zähne, ein zweiter
setzte ihm das Knie auf die Brust und goß ihm solange Branntwein
ein, bis sein Gesicht so rot und der Ausdruck desselben so furchtbar
wurde, daß die übermütigen Quäler sich selbst davor entsetzten. Sie
gaben ihm etwas Luft, und gleich hatte er sie mit einer wütenden
Anstrengung abgeschüttelt, sprang auf und ballte die Fäuste ... aber
plötzlich sanken seine Arme, er taumelte und fiel zu Boden. Da
fluchte, stöhnte er, suchte mehrmals vergeblich sich aufzuraffen und
schlief endlich auf dem Fleck ein, auf den er hingestürzt war, im
Hofe, vor der Scheune, schlief bis zum nächsten Morgen, und als er
erwachte, weil ihm die aufgehende Sonne auf die Nase schien, kam
just der Knecht vorbei, der ihm gestern den Branntwein
eingeschüttet hatte. Der wollte schon die Flucht ergreifen, nichts
andres erwartend, als daß Mischka für die gestrige Mißhandlung
Rache üben werde. Statt dessen reckt sich der Bursche, sieht den
andern traumselig an und lallt: ‚Noch einen Schluck!‘
„Sein Abscheu vor dem Branntwein war überwunden.
„Bald darauf, an einem Sonntag nachmittag, begab es sich, daß
meine Großmutter auf ihrer Spazierfahrt, von einem hübschen
Feldweg gelockt, ausstieg und bei Gelegenheit dieser Wanderung
eine idyllische Szene belauschte. Sie sah Mischka unter einem
Apfelbaum am Feldrain sitzen, ein Kindlein in seinen Armen. Wie er
selbst, hatte auch das Kind den Kopf voll dunkelbrauner Löckchen,
der wohlgebildete kleine Körper hingegen war von lichtbrauner Farbe
und das armselige Hemdchen, das denselben notdürftig bedeckte,
hielt die Mitte zwischen den beiden Schattierungen. Der kleine Balg
krähte förmlich vor Vergnügen, so oft ihn Mischka in die Höhe
schnellte, stieß mit den Füßchen gegen dessen Brust, und suchte
ihm mit dem ausgestreckten Zeigefinger in die Augen zu fahren. Und
Mischka lachte und schien sich mindestens ebensogut zu unterhalten
wie das Bübchen. Dem Treiben der beiden sah ein junges Mädchen
zu, auch ein braunes Ding und so zart und zierlich, als ob ihre Wiege
am Ganges gestanden hätte. Sie trug über dem geflickten kurzen
Rocke eine ebenfalls geflickte Schürze und darin einen kleinen Vorrat
aufgelesener Ähren. Nun brach sie eine derselben vom Stiele, schlich
sich an Mischka heran und ließ ihm die Ähre zwischen der Haut und
dem Hemd ins Genick gleiten. Er schüttelte sich, setzte das Kind auf
den Boden und sprang dem Mädchen nach, das leicht und hurtig
und ordentlich wie im Tanze vor ihm floh; einmal pfeilgerade, dann
wieder einen Garbenschober umkreisend, voll Ängstlichkeit und
dabei doch neckend und immer höchst anmutig. Allerdings ist bei
unsren Landleuten eine gewisse angeborene Grazie nichts Seltenes,
aber diese beiden jungen Geschöpfe gewährten in ihrer harmlosen
Lustigkeit ein so angenehmes Schauspiel, daß meine Großmutter es
mit wahrem Wohlgefallen genoß. Einen andern Eindruck brachte
hingegen ihr Erscheinen auf Mischka und das Mädchen hervor. Wie
versteinert standen beide beim Anblick der Gutsherrin. Er, zuerst
gefaßt, neigte sich beinahe bis zur Erde, sie ließ die Schürze samt
den Ähren sinken und verbarg das Gesicht in den Händen.
„Beim Souper, an dem, wie an jeder Mahlzeit, der Hofstaat,
bestehend aus einigen armen Verwandten und aus den Spitzen der
gräflichen Behörden, teilnahm, sagte meine Großmutter zum Herrn
Direktor, der neben ihr saß: ‚Die Schwester des Mischka, des neuen
Gartenarbeiters, scheint mir ein nettes, flinkes Mädchen zu sein, und
ich wünsche, es möge für die Kleine ein Posten ausgemittelt werden,
an dem sie sich etwas verdienen kann.‘ Der Direktor erwiderte: ‚Zu
Befehl, hochgräfliche Gnaden, sogleich ... obwohl der Mischka
meines Wissens eine Schwester eigentlich gar nicht hat.‘
„‚Ihres Wissens,‘ versetzte meine Großmutter, ‚das ist auch etwas,
Ihr Wissen!... Eine Schwester hat Mischka und ein Brüderchen. Ich
habe heute alle drei auf dem Felde gesehen.‘
„‚Hm, hm,‘ lautete die ehrerbietige Entgegnung, und der Direktor
hielt die Serviette vor den Mund, um den Ton seiner Stimme zu
dämpfen, ‚es wird wohl – ich bitte um Verzeihung des obszönen
Ausdrucks, die Geliebte Mischkas und, mit Respekt zu sagen, ihr
Kind gewesen sein.‘“
Der unwilligen Zuhörerin dieser Erzählung wurde es immer
schwerer, an sich zu halten, und sie rief nun: „Sie behaupten, daß
Sie nicht dabei waren, als diese denkwürdigen Reden gewechselt
wurden? Woher wissen Sie denn nicht nur über jedes Wort, sondern
auch über jede Miene und Gebärde zu berichten?“
„Ich habe die meisten der Beteiligten gekannt, und weiß – ein
bißchen Maler, ein bißchen Dichter, wie ich nun einmal bin – weiß
aufs Haar genau, wie sie sich in einer bestimmten Lage benommen
und ausgedrückt haben müssen. Glauben Sie Ihrem treuen
Berichterstatter, daß meine Großmutter nach der Mitteilung, welche
der Direktor ihr gemacht, eine Wallung des Zornes und der
Menschenverachtung hatte. Wie gut und fürsorglich für ihre
Untertanen sie war, darüber können Sie nach dem bisher Gehörten
nicht in Zweifel sein. Im Punkte der Moral jedoch verstand sie nur
äußerste Strenge, gegen sich selbst nicht minder als gegen andre.
Sie hatte oft erfahren, daß sie bei Männern und Frauen der
Sittenverderbnis nicht zu steuern vermöge, der Sittenverderbnis bei
halbreifen Geschöpfen jedoch, der mußte ein Zügel angelegt werden
können. – Meine Großmutter schickte ihren Kammerdiener wieder zu
den Eltern Mischkas. Mit der Liebschaft des Burschen habe es aus zu
sein. Das sei eine Schande für so einen Buben, ließ sie sagen, ein
solcher Bub habe an andre Dinge zu denken.
„Der Mischka, der zu Hause war, als die Botschaft kam, schämte
sich in seine Haut hinein ...“
„Es ist doch stark, daß Sie jetzt gar in der Haut Mischkas stecken
wollen!“ fuhr die Gräfin höhnisch auf.
„Bis über die Ohren!“ entgegnete der Graf, „bis über die Ohren
steck ich darin! Ich fühle, als wäre ich es selbst, die Bestürzung und
Beschämung, die ihn ergriff. Ich sehe ihn, wie er sich windet in
Angst und Verlegenheit, einen scheuen Blick auf Vater und Mutter
wirft, die auch nicht wissen, wo ein und aus vor Schrecken, ich höre
sein jammervoll klingendes Lachen bei den Worten des Vaters:
‚Erbarmen Sie sich, Herr Kammerdiener! Er wird ein Ende machen,
das versteht sich, gleich wird er ein Ende machen!‘“
„Diese Versicherung genügte dem edlen Fritz, er kehrte ins Schloß
zurück und berichtete, glücklich über die treffliche Erfüllung seiner
Mission, mit den gewohnten Kniebeugungen und dem gewohnten
demütigen und freudestrahlenden Ausdruck in seiner
Vogelphysiognomie: ‚Er laßt die Hand küssen, er wird ein Ende
machen.‘“
„Lächerlich!“ sagte die Gräfin.
„Höchst lächerlich!“ bestätigte der Graf. „Meine gute,
vertrauensselige Großmutter hielt die Sache damit für abgetan,
dachte auch nicht weiter darüber nach. Sie war sehr in Anspruch
genommen durch die Vorbereitungen zu den großen Festen, die
alljährlich am zehnten September, ihrem Geburtstage, im Schlosse
gefeiert wurden, und einen Vor- und Nachtrab von kleinen Festen
hatten. Da kam die ganze Nachbarschaft zusammen, und Dejeuners,
auf dem grünen Teppich der Wiesen, Jagden, Pirutschaden, Soupers
bei schönster Waldbeleuchtung, Bälle – und so weiter folgten
einander in fröhlicher Reihe ... Man muß gestehen, unsre Alten
verstanden Platz einzunehmen und Lärm zu machen in der Welt.
Gott weiß, wie langweilig und öde unser heutiges Leben auf dem
Schlosse ihnen erscheinen müßte.“
„Sie waren eben große Herren,“ entgegnete die Gräfin bitter, „wir
sind auf das Land zurückgezogene Armenväter.“
„Und – Armenmütter,“ versetzte der Graf mit einer galanten
Verneigung, die von derjenigen, der sie galt, nicht eben gnädig
aufgenommen wurde. Der Graf aber nahm sich das Mißfallen, das er
erregt hatte, keineswegs zu Herzen, sondern spann mit hellem
Erzählerbehagen den Faden seiner Geschichte fort:
„So groß der Dienertroß im Schlosse auch war, während der Dauer
der Festlichkeiten genügte er doch nicht, und es mußten da immer
Leute aus dem Dorfe zur Aushilfe requiriert werden. Wie es kam,
daß sich gerade dieses Mal auch Mischkas Geliebte unter ihnen
befand, weiß ich nicht, genug, es war der Fall, und die beiden
Menschen, die einander hätten meiden sollen, wurden im Dienste
der Gebieterin noch öfter zusammengeführt, als dies in früheren
Tagen bei der gemeinsamen Feldarbeit geschehen war. Er, mit einem
Botengang betraut, lief vom Garten in die Küche, sie von der Küche
in den Garten – manchmal trafen sie sich auch unterwegs und
verweilten plaudernd ein Viertelstündchen ...“
„Äußerst interessant!“ spottete die Gräfin – „wenn man doch nur
wüßte, was sie einander gesagt haben.“
„O, wie Sie schon neugierig geworden sind! – aber ich verrate
Ihnen nur, was unumgänglich zu meiner Geschichte gehört. – Eines
Morgens lustwandelte die Schloßfrau mit ihren Gästen im Garten.
Zufällig lenkte die Gesellschaft ihre Schritte nach einem selten
betretenen Laubgang und gewahrte am Ende desselben ein junges
Pärchen, das, aus verschiedenen Richtungen kommend, wie freudig
überrascht stehen blieb. Der Bursche, kein andrer als Mischka, nahm
das Mädchen rasch in die Arme und küßte es, was es sich ruhig
gefallen ließ. Ein schallendes Gelächter brach los – von den Herren
und, ich fürchte, auch von einigen der Damen ausgestoßen, die der
Zufall zu Zeugen dieses kleinen Auftritts gemacht hatte. Nur meine
Großmutter nahm nicht teil an der allgemeinen Heiterkeit. Mischka
und seine Geliebte stoben natürlich davon. Der Bursche – man hat
es mir erzählt“ – kam der Graf scherzend einer voraussichtlichen
Einwendung der Gräfin entgegen, „glaubte in dem Augenblick sein
armes Mädchen zu hassen. Am selben Abend jedoch überzeugte er
sich des Gegenteils, als er nämlich erfuhr, die Kleine werde mit ihrem
Kinde nach einer andern Herrschaft der Frau Gräfin geschickt; zwei
Tagereisen weit für einen Mann, für eine Frau, die noch dazu ein
anderthalb Jahre altes Kind mitschleppen mußte, wohl noch einmal
so viel. – Mehr als: ‚Herrgott! Herrgott! o du lieber Herrgott!‘ sprach
Mischka nicht, gebärdete sich wie ein Träumender, begriff nicht, was
man von ihm wolle, als es hieß an die Arbeit gehen – warf plötzlich
den Rechen, den ein Gehilfe ihm samt einem erweckenden
Rippenstoß verabfolgte, auf den Boden, und rannte ins Dorf, nach
dem Hüttchen, in dem seine Geliebte bei ihrer kranken Mutter
wohnte, das heißt, gewohnt hatte, denn nun war es damit vorbei.
Die Kleine stand reisefertig am Lager der völlig gelähmten Alten, die
ihr nicht einmal zum Abschiedsegen die Hand aufs Haupt legen
konnte, und die bitterlich weinte. ‚Hört jetzt auf zu weinen,‘ sprach
die Tochter, ‚hört auf, liebe Mutter. Wer soll euch denn die Tränen
abwischen, wenn ich einmal fort bin?‘
„Sie trocknete die Wangen ihrer Mutter und dann auch ihre
eigenen mit der Schürze, nahm ihr Kind an die Hand und das Bündel
mit ihren wenigen Habseligkeiten auf den Rücken und ging ihres
Weges an Mischka vorbei, und wagte nicht einmal, ihn anzusehen.
Er aber folgte ihr von weitem, und als der Knecht, der dafür zu
sorgen hatte, daß sie ihre Wanderung auch richtig antrete, sie auf
der Straße hinter dem Dorfe verließ, war Mischka bald an ihrer Seite,
nahm ihr das Bündel ab, hob das Kind auf den Arm und schritt so
neben ihr her.
„Die Feldarbeiter, die in der Nähe waren, wunderten sich: – ‚Was
tut er denn, der Tropf?... Geht er mit? Glaubt er, weil er so dumm
ist, daß er nur so mitgehen kann?‘
„Bald nachher kam keuchend und schreiend der Vater Mischkas
gerannt: ‚O, ihr lieben Heiligen! Heilige Mutter Gottes! hab ich mir's
doch gedacht – seiner Dirne läuft er nach, bringt uns noch alle ins
Unglück ... Mischka! Sohn – mein Junge!... Nichtsnutz! Teufelsbrut!‘
– jammerte und fluchte er abwechselnd.
„Als Mischka die Stimme seines Vaters hörte und ihn mit drohend
geschwungenem Stocke immer näher herankommen sah, ergriff er
die Flucht, zur größten Freude des Knäbleins, das ‚Hott! hott!‘
jauchzte. Bald jedoch besann er sich, daß er seine Gefährtin, die ihm
nicht so rasch folgen konnte, im Stich gelassen, wandte sich und lief
zu ihr zurück. Sie war bereits von seinem Vater erreicht und zu
Boden geschlagen worden. Wie wahnsinnig raste der Zornige, schlug
drein mit den Füßen und mit dem Stocke, und ließ seinen ganzen
Grimm über den Sohn an dem wehrlosen Geschöpfe aus.
„Mischka warf sich dem Vater entgegen, und ein furchtbares
Ringen zwischen den beiden begann, das mit der völligen Niederlage
des Schwächeren, des Jüngeren, endete. Windelweich geprügelt,
aus einer Stirnwunde blutend, gab er den Kampf und den
Widerstand auf. Der Häusler faßte ihn am Hemdkragen und zerrte
ihn mit sich; der armen kleinen Frau aber, die sich inzwischen
mühsam aufgerafft hatte, rief er zu: ‚Mach fort!‘
„Sie gehorchte lautlos, und selbst die Arbeiter auf dem Felde,
stumpfes, gleichgültiges Volk, fühlten Mitleid und sahen ihr lange
nach, wie sie so dahinwankte mit ihrem Kinde, so hilfsbedürftig und
so völlig verlassen.
„In der Nähe des Schlosses trafen Mischka und sein Vater den
Gärtner, den der Häusler sogleich als ‚gnädiger Herr‘ ansprach und
flehentlich ersuchte, nur eine Stunde Geduld zu haben mit seinem
Sohne. In einer Stunde werde Mischka gewiß wieder bei der Arbeit
sein; jetzt müsse er nur geschwind heimgehen und sich waschen
und sein Hemd auch. Der Gärtner fragte: ‚Was ist ihm denn? er ist ja
ganz blutig.‘ – ‚Nichts ist ihm,‘ lautete die Antwort, ‚er ist nur von der
Leiter gefallen.‘
„Mischka hielt das Wort, das sein Vater für ihn gegeben, und war
eine Stunde später richtig wieder bei der Arbeit. Am Abend aber ging
Welcome to our website – the perfect destination for book lovers and
knowledge seekers. We believe that every book holds a new world,
offering opportunities for learning, discovery, and personal growth.
That’s why we are dedicated to bringing you a diverse collection of
books, ranging from classic literature and specialized publications to
self-development guides and children's books.

More than just a book-buying platform, we strive to be a bridge


connecting you with timeless cultural and intellectual values. With an
elegant, user-friendly interface and a smart search system, you can
quickly find the books that best suit your interests. Additionally,
our special promotions and home delivery services help you save time
and fully enjoy the joy of reading.

Join us on a journey of knowledge exploration, passion nurturing, and


personal growth every day!

ebookbell.com

You might also like