Instructions for aiAssist

(produced at 23:21 UTC on 2024-03-10)

This task is part of project07 which is due at 23:00 EDT on 2024-03-15.

You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.

You can download the starter code for this task using this link.

You can submit this task using this link.

Put all of your work for this task into the file aiAssist.py
(which is provided among the starter files)

This tasks asks you to test out AI coding capabilities, by writing a few different functions with the help of an AI chatbot. After working with the AI, you'll fill out a survey where you get to judge the value of different prompts. Note that normally, you are NOT allowed to use AI chatbots to help with your projects, because besides the ethical issues with using them at all, we have good evidence that using them would severely limit your learning. This task is special, in that we are giving you a chance to see why they are otherwise forbidden.

Setup & Ethical Issues

Note: If, based on the ethical issues we've been discussing in this course, you'd rather not sign up for or use a chatbot, that's fine. We'll provide links to some pre-constructed prompt sessions that you can use as the basis of your work instead. See below for a discussion of our attempts to mitigate the ethical concerns with AI use for this assignment.

To start with, you should sign up for the Claude AI using that link.

Important: Claude limits the number of messages you can send on the free plan per day. You should allow sufficient time to send enough prompts to complete this assignment.

At minimum, you will need to send about 2-4 messages. This should be well within the limits of a free account, but if you send more messages prompting for follow-ups or to improve the code, you may hit the limit. You can always view our saved prompt sessions included in these instructions as a fall-back.

Note that all large language model systems, including Claude, ChatGPT, and Github Copilot, present several ethical issues with their use:

  1. The training data for these systems is usually harvested without the consent of those who created it, and is often used despite their explicit non-consent. The companies creating these systems do so in a way that does not respect the explicit sharing parameters specified by the authors of the data going into them, even though it is possible (although more costly) to train system in a way that would respect the rights and wishes of creators.
  2. Training data is harvested without regard to licences. The output of a chatbot is a mix of words connected via rules learned from many examples, and sometimes, words connected in a way which exactly duplicates a single specific example. This means that these chatbots sometimes suggest code or prose that should come with a specific license, without letting the user know that it is doing so or that there is a license. For example, open-source code under a license which permits sharing but requires attribution may be suggested verbatim by a chatbot. Someone using the suggested code would be liable for license infringement if they did not include attribution, which would be difficult since they'd have no warning from the chatbot that the code was under license. This is the biggest legal reason not to use AI chatbots, especially when writing code. For this assignment, you are only sharing your code with us, the instructors, and you're doing it for educational purposes, so the "fair use" provisions of copyright apply. They would not apply if you were writing code for a job.
  3. Even for systems whose training data is acquired only via explicit consent (these are extremely rare and we don't know of one that's publicly available), the training data needs to be filtered. The internet is full of absolutely horrific images, stories, and other material, and to avoid these becoming part of the chatbot's output, humans must look at this material and flag it. Similar to content moderation for a big platform like YouTube, the job of reading through and flagging these horrific things is mentally draining and potentially traumatic. To put it plainly: the relative "safety" with which we can use AI chatbots is bought by the traumatization of workers who filter out the horrific stuff. Rather than compensate these workers well for their time and give them plenty of time off and mental health support, companies outsource this work to desperate people and then force them to work long hours with little support. This is the reason that we're asking you to use Claude instead of ChatGPT for this assignment: it does not use human workers to flag problematic content (although its AI-based methodology for doing so is probably much more limited than the company claims).
  4. Finally, despite the efforts made to prevent chatbots from echoing the most hateful and traumatizing sentiments from their datasets, these filtering tools are not perfect, and there is still the chance that these things come out. Additionally, the chatbots reflect and even amplify more accepted forms of bias in our society, and so their use perpetuates these biases. Biases such as ablism, sanism, fatmisia, and more sanitized forms of sexism and racism (such as assuming someone's gender or race based on their profession) all go unfiltered in the data cleaning process and manifest in the outputs of the AI. We think that the chances of these biases coming into play significantly in this particular assignment are low, as we've tried to focus things on purely mechanical coding. But there is no such thing as "politically neutral" code, as long as your code is going to be used by humans. Please do bring to our attention any biases that you see manifesting in the outputs you get while working on this task.
  5. The servers used to train and run these AI tools consume huge amounts of electricity, along with water for cooling. While they aren't exactly a leading cause of CO2 emissions, they are not making the problem better, especially when put to frivolous use. We think that the resource costs associated with students using AI for this task are an acceptable tradeoff based on the importance of the learning goals, and those goals require us to use AI rather than some other substitute. But this is yet another reason that we don't casually use AI in our day-to-day programming (in addition to the reasons above and the fact that it just isn't that helpful in a lot of circumstances, as you're about to experience).

As described above, we've taken steps to mitigate the impacts of these ethical issues for the purpose of this specific assignment. If for some reason you still don't feel comfortable using AI for this assignment, please reach out to Prof. Mawhorter (pmawhort@wellesley.edu) and we can discuss alternative possibilities. We do take these ethical issues seriously!

There's one additional technical issue that will come up on this assignment: chatbots are not dependable. Because they run on other company's servers, those servers could go down or reach capacity just when you need them (and we've already mentioned the message limit of Claude). In the longer term, venture capital funding is right now enabling lots of free AI services. But all of those are going to eventually need to make money and become paid services or otherwise try to extract value from their users in ways that may not be acceptable to you. This means that if we taught you to code only using AI chatbots now, you'd be stuck later when those chatbots start demanding money for their use. In the scope of this assignment, please get started early and try not to work right against the deadline. The Claude chatbot sometimes responds with an error message saying that it doesn't have capacity to answer your request, and we don't want you to have to write this code without using it.

Overview

In this task, we'll ask you to use AI to solve a problem that you've already solved: the diamonds task. As it turns out, the AI is not very good at this problem.

The Potluck evaluation will be based on successfully solving the diamonds task, which you have already solved. Note that as normal, you will need to include docstrings for each function you define, including the function(s) for any extra goals. The AI predictions often do not include docstrings, so you may have to add these yourself (or prompt the AI to predict them).

You will also need to individually submit answers to a survey by filling out this form. (If you are working with a partner, both of you need to submit the survey individually.) We will be discussing these answers in class, but there are no right or wrong answers, you need to complete the survey but you won't be graded on whether your answers are "correct" or not.

We have included an OPTIONAL extra part of this task: a problem that uses some complex loops and dictionaries (which you haven't seen yet). We do not expect you to be able to solve this task even using AI, because if you don't understand the basic programming concepts the AI is using you won't be able to fix the code it produces.

Diamond Pattern

The diamond pattern task can be hard for the AI to generate successful predictions for, because its input capabilities don't process text exactly as humans can (and in particular, it may sometimes ignore spacing, which is important for this task). We have included three sample prompts below, feel free to use the one that you think will be most successful, or you can try copying and pasting from the original problem description. Each prompt is included in a text box for easy copying and pasting, and has an example prompt session below it if you'd prefer not to use the AI yourself.

This first prompt is the most detailed, and required a very thorough understanding of what each function was supposed to do in order to create:

Click here to see an example prompt session with this prompt.

Initial prompt:

What follows is the Python code for printing a diamond pattern. The code
uses 3 functions: diamondPattern prints the whole pattern (and has no
arguments), but it does not directly call print, and instead calls a
helper function called rowOfDiamonds. That function has one argument,
called "prefix" which specifies the string to use as a prefix for each
line of the row. In turn, rowOfDiamonds calls the numbersRow function,
which prints one row of numbers using two arguments: "prefix", and
"numberString." The arguments use only the provided strings one, two,
three, empty, and zero:

one = "  1  "

two = " 2 2 "

three = "3 3 3"

empty = "     " # 5 spaces

zero = ""

The code doesn't use any other strings. Through the function calls it
makes, diamondPattern prints out a pattern of diamond-shaped numbers with
spaces in between, as follows: Each "diamond row" is made up of 5 rows of
text, the first and last using the "one" string to include ones, the
second and fourth using the "two" string to include twos, and the middle
row using the "three" string to include threes. In each of these lines of
text, each copy of the relevant number string is separated from the
others by 5 spaces (the "empty" string). There are a total of four number
string copies on each line of text, to create a visual pattern of four
diamonds when the entire "diamond row" is printed. The entire pattern
consists of eight such diamond rows, with every other row (starting from
the second) being indented 5 spaces, so that the diamonds from one
"diamond row" sit above the gaps in the row below. Since there are 8
diamond rows, each with 5 lines of text, there are a total of 40 lines of
output.

The code for this is shown next:

The first prediction from the AI:

Here is my understanding of the Python code to print a diamond pattern:

The code uses 3 functions - diamondPattern, rowOfDiamonds, and numbersRow.

diamondPattern is the main function that prints the full diamond pattern.
It does not directly call print, but instead calls rowOfDiamonds.

rowOfDiamonds takes 1 argument - prefix. It prints one "diamond row" by
calling numbersRow 5 times with different parameters.

numbersRow takes 2 arguments - prefix and numberString. It prints one row
of text using the provided prefix and numberString.

There are 5 predefined strings used - one, two, three, empty, and zero.

diamondPattern calls rowOfDiamonds 8 times to print 8 diamond rows. The
even numbered rows are indented by prefixing empty (5 spaces).

Each diamond row calls numbersRow 5 times - first and last with one,
second and fourth with two, middle with three. empty is used between the
numberStrings.

So in total there are 8 diamond rows printed, each with 5 lines of text,
resulting in 40 lines of output in a diamond pattern.

Does this accurately summarize the key points of how the code prints the
diamond pattern? Let me know if you would like me to clarify or expand on
any part.

Our response, to elicit actual code:

The code for the diamondPattern, rowOfDiamonds, and numbersRow functions
is:

The prediction from the AI containing code:

Here is my understanding of the full code:
def diamondPattern():
  rowOfDiamonds(zero)
  rowOfDiamonds(empty) 
  rowOfDiamonds(zero)
  rowOfDiamonds(empty)
  rowOfDiamonds(zero) 
  rowOfDiamonds(empty)
  rowOfDiamonds(zero)
  rowOfDiamonds(empty)

def rowOfDiamonds(prefix):
  numbersRow(prefix, one)
  numbersRow(prefix, two)
  numbersRow(prefix, three) 
  numbersRow(prefix, two)
  numbersRow(prefix, one)

def numbersRow(prefix, numberString):
  print(prefix + numberString + empty + numberString + empty + numberString + empty + numberString)

one = " 1 " 
two = " 2 2 "
three = "3 3 3"
empty = "     " 
zero = ""
diamondPattern is the main function that calls rowOfDiamonds 8 times. 

rowOfDiamonds takes a prefix argument, and calls numbersRow 5 times to
print a diamond row.

numbersRow prints one row using the provided prefix and numberString.

The strings one, two, three, empty, and zero are used to build the
pattern.

Does this match your understanding of how the full code works to print
the diamond pattern? Let me know if you need any clarification!

Note: the predicted text uses the word "understanding" but the AI system does not in fact "understand" in a meaningful way, even though it is absorbing and retaining some facets of the problem description in order to create its prediction and to inform subsequent predictions. Rather than actually "understanding" the problem, it has predicted that a human would likely say "my understanding is" in this situation, and that's why it generates that text as part of its prediction. This logic applies to every interaction with an AI: do not take its human-like statements of emotion or cognition at face value, and instead read through that it has predicted a human would make such a statement in the given circumstance.

This second prompt was less detailed than the one above. We're asking the AI to actually make a more specific prediction of code based on a little less detailed information.

Click here to see an example prompt session with this prompt.

Our initial prompt:

What follows is the Python code for printing a diamond pattern. The code
uses 3 functions: diamondPattern prints the whole pattern (and has no
arguments), but it does not directly call print, and instead calls a
helper function called rowOfDiamonds. That function has one argument,
called "prefix" which specifies the string to use as a prefix for each
line of the row. In turn, rowOfDiamonds calls the numbersRow function,
which prints one row of numbers using two arguments: "prefix", and
"numberString." The arguments use only the provided strings one, two,
three, empty, and zero:

one = "  1  " # two spaces on either side

two = " 2 2 "

three = "3 3 3"

empty = "     " # 5 spaces

zero = ""

The code doesn't use any other strings. Through the function calls it
makes, diamondPattern prints out a pattern of diamond-shaped numbers with
spaces in between. The entire pattern consists of eight diamond rows,
with every other row (starting from the second) being indented 5 spaces,
so that the diamonds from one "diamond row" sit above the gaps in the row
below. Each row contains 4 diamonds, and each diamond sets the 'one',
'two', and 'three' strings above each other to create a diamond shape
(re-using 'two' and 'one' below). Since there are 8 diamond rows, each
with 5 lines of text, there are a total of 40 lines of output. Here's
what that looks like:

  1         1         1         1  
 2 2       2 2       2 2       2 2 
3 3 3     3 3 3     3 3 3     3 3 3
 2 2       2 2       2 2       2 2 
  1         1         1         1  
       1         1         1         1  
      2 2       2 2       2 2       2 2 
     3 3 3     3 3 3     3 3 3     3 3 3
      2 2       2 2       2 2       2 2 
       1         1         1         1  
  1         1         1         1  
 2 2       2 2       2 2       2 2 
3 3 3     3 3 3     3 3 3     3 3 3
 2 2       2 2       2 2       2 2 
  1         1         1         1  
       1         1         1         1  
      2 2       2 2       2 2       2 2 
     3 3 3     3 3 3     3 3 3     3 3 3
      2 2       2 2       2 2       2 2 
       1         1         1         1  
  1         1         1         1  
 2 2       2 2       2 2       2 2 
3 3 3     3 3 3     3 3 3     3 3 3
 2 2       2 2       2 2       2 2 
  1         1         1         1  
       1         1         1         1  
      2 2       2 2       2 2       2 2 
     3 3 3     3 3 3     3 3 3     3 3 3
      2 2       2 2       2 2       2 2 
       1         1         1         1  
  1         1         1         1  
 2 2       2 2       2 2       2 2 
3 3 3     3 3 3     3 3 3     3 3 3
 2 2       2 2       2 2       2 2 
  1         1         1         1  
       1         1         1         1  
      2 2       2 2       2 2       2 2 
     3 3 3     3 3 3     3 3 3     3 3 3
      2 2       2 2       2 2       2 2 
       1         1         1         1

The code for diamondPattern, rowOfDiamonds, and numbersRow is:

The first prediction from the AI:

Here is the Python code to generate the diamond pattern described:

def diamondPattern():
  rowOfDiamonds(empty)
  rowOfDiamonds(one) 
  rowOfDiamonds(empty)
  rowOfDiamonds(one)
  rowOfDiamonds(empty) 
  rowOfDiamonds(one)
  rowOfDiamonds(empty)
  rowOfDiamonds(one)

def rowOfDiamonds(prefix):
  numbersRow(prefix, one)
  numbersRow(prefix, two)
  numbersRow(prefix, three) 
  numbersRow(prefix, two)
  numbersRow(prefix, one)

def numbersRow(prefix, numberString):
  print(prefix, numberString, prefix, numberString, prefix, numberString, prefix, numberString)

The key points:

- diamondPattern calls rowOfDiamonds 8 times total, alternating empty and
  one as the prefix
- rowOfDiamonds prints 5 rows each time, calling numbersRow with
  different numberStrings
- numbersRow prints the prefix and numberString 4 times each 

This generates the full diamond pattern through nested function calls.

Note that this time the AI did not include a code block; it just wrote code as part of the text reply. Also note that this reply does NOT in fact generate the full diamond pattern correctly. You can attempt to prompt the AI for a better solution, but it's likely less work to simply fix this solution yourself, since it's close to correct.

This third prompt is

Click here to see an example prompt session with this prompt.

Our initial prompt:

The following Python code defines three functions, only one of which uses
print, that together create a diamond pattern. The code does not use any
conditionals or loops. The pattern that the code prints has 40 lines and
looks like this:

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

1 1 1 1

2 2 2 2 2 2 2 2

3 3 3 3 3 3 3 3 3 3 3 3

2 2 2 2 2 2 2 2

1 1 1 1

Note how after copy-pasting the precise diamond pattern, the input part of the AI system re-formats it, destroying the pattern. We sometimes had success doing a separate copy-paste of just the pattern part, but we sometimes did not. You could also try using the attachment option to attach a text file, which we did not try.

The prediction from the AI is totally wrong for this prompt:

Here is how I would implement the diamond pattern printing code without
any loops or conditionals:
def line(char, count):
    return char * count

def diamonds(n):
    for i in range(n):
        print(line(str(n - i), i + 1))
    for i in range(n - 1, 0, -1):
        print(line(str(i), n - i + 1))

def main():
    diamonds(3)

main()
The `line` function simply repeats a character a given number of times. 

The `diamonds` function uses two range loops going in opposite directions
to print the increasing and decreasing line lengths.

And `main` calls `diamonds` with the diamond size to print the full pattern.

This implements the diamond pattern by leveraging the range loops and
avoiding any explicit conditionals or counting loops.

You can attempt to continue this line of prompting, but in our attempts it was nearly impossible to get the AI to succeed without giving it much more specific instructions, as in the previous prompts.

Note especially how its prediction has avoided "any explicit conditionals or counting loops" but that our explicit instructions were to avoid loops entirely. This is a good example of how the AI merely predicts and does not understand. There is no part of any prompt you give it that it will always fully follow, and it may instead decide to effectively discard or modify parts of your instructions because it determines that a response which does not really follow that part of the instructions is more likely overall. This is one reason that using an AI to do most tasks is counter productive, as you need to check its work so carefully you might as well have done the work yourself.

Note that you are responsible for coming up with a working dimaondPattern function, even if the AI is not giving you useful predictions. We have relaxed the constraints a bit from the original problem, requiring only that the diamondPattern function itself not use print, loops, or conditionals, since this time your file will contain many other functions.

The specific requirements are:

  • Define a zero-parameter function called diamondPattern which makes between 2 and 8 calls to another custom function.
  • Calling diamondPattern should print the same 40-line pattern specified in the original diamonds task instructions.
  • Neither diamondPattern nor the functions it calls may use any conditionals or loops. The functions it calls should not use more than 10 separate print calls.

You can run the provided test_diamondPattern.py function to check for correctness of your diamond pattern output, although it does not check all of the coding restrictions.

Survey Questions:

Reminder: use this form to fill out the prompt evaluation survey, which asks you to judge how successful you think various prompts will be. Each partner needs to submit separately, but you're only graded on whether you complete it, not on which answers you pick..

Click here if you want to try the OPTIONAL extra part

Wedding Seating

For this OPTIONAL part, we have create a new assignment involving dictionaries, which are a Python construct we have not yet covered (but which we will cover soon). Although you must define the optimizeSeating function in your solution, it does not have to be correct (it just needs to be defined and have a docstring). The goal of this task is to let you see for yourself whether you can use AI to solve problems that you don't fully understand, or whether you need to learn the underlying material for AI to be useful at all.

The requirements for the function are defined in the prompt below, which you can start by passing directly to the AI. Feel free to attempt follow-up prompts and/or to tweak this prompt and start new conversations to see if you can get the AI to generate a correct solution. Also feel free to start with what it gives you and tweak the code yourself to see if you can get it to work correctly. The provided test_optimizeSeating.py file can be used to check whether your definition is working. Here is the starting prompt; below is an example of our prompt session using this prompt, if you'd prefer not to use the AI directly.

Click here to see an example prompt session with this prompt.

Our initial prompt:

The code below completes the following assignment:

You are planning a wedding, and need to arrange seating for the guests.
However, some of the guests hate or dislike each other. Your job is to
arrange a seating chart where guests that hate each other are seated at
separate tables, and guests that dislike each other may be seated at the
same table, but are not seated next to each other. The wedding seating
chart will be a list of lists, each corresponding to one table and
specifying the different guests who will sit at that table. Each guest is
represented by a dictionary, with "name", "hates", and "dislikes" keys.
Their "name" value will be a string, and their "hates" and "dislikes"
values will each be a list containing references to other guest
dictionaries who they hate or dislike. Your seating list needs to contain
references to these guest dictionaries in each table sub-list. You will
write an "optimizeSeating" function which takes two arguments: a list of
guest dictionaries, and an integer specifying the maximum size of a
single table. It will return a list of table lists, each containing guest
dictionaries.

If a guest has no other guests that they hate, and/or no other guests
that they dislike, their dictionary will not include the relevant key.
This means that your code must work even if some guests are missing one
or both of the "hates" or "dislikes" keys.

Your function must avoid placing any guest at the same table with a guest
that they hate (note that hatred is not always mutual). It must also
avoid placing any guest adjacent to a guest they dislike (two items next
to each other in a table list count as adjacent, and the first and last
items in each table list also count as adjacent). You will use the
following (imperfect) algorithm for this: for each guest in the provided
list, in order, you will scan through the list of tables you have already
set up. If there are no tables yet, or if all existing tables are full,
you will add a new table to your result, seating just that guest. If the
tables are not yet full, you will seat the guest at the first table where
they don't hate (and aren't hated by) any guest there. Additionally, you
will put them into the first spot such that they neither dislike nor are
disliked by either neighbor of that spot (if there is no such spot, seat
them at a different table). If there is no table they can be seated at
while respecting the hates and dislikes restrictions, then you will once
again create a new table and seat them there. There are better algorithms
for solving this problem with fewer (and less unbalanced) tables, but you
must use the algorithm described here in your solution.

The code for the "optimizeSeating" function is:

The first prediction from the AI:

Here is one way to implement the optimizeSeating function:
def optimizeSeating(guests, maxTableSize):
  tables = []

  for guest in guests:
    seated = False

    for table in tables:
      if len(table) < maxTableSize:
        seats = list(range(len(table) + 1))
        for i in seats:
          if guest.get("hates") and table[i] in guest["hates"]:
            seats.remove(i)
          elif table[i].get("hates") and guest in table[i]["hates"]:
            seats.remove(i)
          elif i > 0 and guest.get("dislikes") and table[i-1] in guest["dislikes"]:
            seats.remove(i)  
          elif i < len(table)-1 and guest.get("dislikes") and table[i+1] in guest["dislikes"]:
            seats.remove(i)

        if seats:
          table.insert(seats[0], guest)
          seated = True
          break

    if not seated:
      tables.append([guest])

  return tables
This loops through each guest and tries to find an existing table where
they can be seated without breaking the hates/dislikes constraints. It
checks each seat at the table by index to avoid neighbors that are
disliked. If no valid seat is found, it adds a new table just for that
guest.

Some things to note:

- It uses list insert() rather than append() to insert guests at specific
  indexes rather than just adding them to the end
- It handles guests missing hates/dislikes keys by using .get() with
  default values
- It checks both directions of hatred since hatred is not necessarily mutual

Let me know if you have any other questions!

Given that this code has some bugs in it, we provided the following follow-up prompt:

The code above does not account for guests disliking a neighbor at the
other end of the list. It also does not work because it generates an
IndexError since it tries to consider adding guests at the end of the
list but fails to access the existing table elements properly in this
case. A better version of the function is:

The responding prediction from the AI was:

You're absolutely right, my original code had some flaws in properly
handling the end seats of a table. Here is an updated version that should
fully handle the end seat cases and avoid index errors:
def optimizeSeating(guests, maxTableSize):
  tables = []

  for guest in guests:
    seated = False

    for table in tables:
      if len(table) < maxTableSize:
        seats = list(range(len(table) + 1))

        # Check dislikes at ends of table
        if table and guest.get("dislikes") and table[-1] in guest["dislikes"]: 
          seats.remove(len(table))
        if len(table) > 1 and guest.get("dislikes") and table[0] in guest["dislikes"]:
          seats.remove(0)

        for i in seats:
          # Check hates
          if guest.get("hates") and table[i] in guest["hates"]:
            seats.remove(i)
          elif table[i].get("hates") and guest in table[i]["hates"]:
            seats.remove(i)

          # Check neighbor dislikes
          if i > 0 and guest.get("dislikes") and table[i-1] in guest["dislikes"]:
            seats.remove(i)
          elif i < len(table)-1 and guest.get("dislikes") and table[i+1] in guest["dislikes"]:
            seats.remove(i)

        if seats:  
          table.insert(seats[0], guest)
          seated = True
          break

    if not seated:  
      tables.append([guest])

  return tables
The key changes:

- Check dislikes with end seats explicitly 
- Only check neighbor dislikes if not at the ends
- Use len(table) instead of hardcoded indexes to avoid index errors

Let me know if this improved version handles all the edge cases correctly!

This prediction does not fix the IndexError in the original code, and its attempt to address dislikes for seating at the start or end of the table list is flawed. So we gave it one more prompt:

This version still fails with an IndexError, when tested using:

print(optimizeSeating([
{'name': 'Julia', 'hates': [], 'dislikes': []},
{'name': 'Yasmin', 'hates': [], 'dislikes': ['Julia']},
{'name': 'Aaron', 'hates': [], 'dislikes': []},
], 3))

Code that does not generate an error would be:

Note that the AI, unlike a human, does NOT have the ability to run code and see what happens, so providing it with this test case is not really productive. The third prediction from the AI demonstrates this:

You're absolutely right, thanks for catching that with a specific test
case! Index errors can be tricky to fully avoid. Here is a version that
should now avoid index errors in all cases:
def optimizeSeating(guests, maxTableSize):
  tables = []

  for guest in guests:
    seated = False

    for table in tables:
      if len(table) < maxTableSize:
        seats = list(range(len(table) + 1))

        # Check end dislikes
        if table and guest.get("dislikes") and table[-1] in guest["dislikes"]:
          seats.remove(len(table)) 
        if len(seats) > 1 and 0 in seats and guest.get("dislikes") and table[0] in guest["dislikes"]:
          seats.remove(0)

        for i in seats:
          # Check hates
          if i < len(table) and guest.get("hates") and table[i] in guest["hates"]:
            seats.remove(i)
          elif i < len(table) and table[i].get("hates") and guest in table[i]["hates"]:
            seats.remove(i)

          # Check neighbor dislikes  
          if i > 0 and i-1 in seats and guest.get("dislikes") and table[i-1] in guest["dislikes"]:
            seats.remove(i)
          if i+1 in seats and guest.get("dislikes") and table[i+1] in guest["dislikes"]:
            seats.remove(i)

        if seats:
          table.insert(seats[0], guest)
          seated = True
          break

    if not seated:
      tables.append([guest])

  return tables
The key changes:

- Check table length before indexing to avoid going out of bounds
- Only check neighbor if that index is still in seats list
- Wrap end checks in len() to avoid negatives

Let me know if this version handles all cases correctly!

This code still generates the same IndexError due to the same issue. In testing for this assignment we've had other prompt sessions that were more successful in avoiding outright crashes, but none that succeeded in handling the edge cases correctly. Although this assignment is of a reasonably complexity for someone who has actually learned about dictionaries in CS 111, it's apparently NOT at a level that the AI can solve. This isn't too surprising, since the AI is only trying to make predictions based on code others have written. This is a new problem it hasn't seen before, and one where stitching together fragments of other people's code isn't good enough to solve it.

Extra Reflection Questions:

(You don't need to submit these ones.)

  • In general, is it easier to debug code that you wrote vs. code that someone else wrote (or which an AI predicted?)?
  • Were you able to solve this problem? If so, did you need to learn about dictionaries (and possibly about statements like break or continue) or were you able to get the AI to predict a correct solution without understanding those things? How was the AI helpful in this process, and where was it distracting or misleading?
  • If you were to publish code from this assignment as part of a program, either commercially or for free, how would you check whether the code violated copyright by including fragments from code that used open-source licenses requiring attribution?

The End

This is the end of this assignment. Make sure to double check your Potluck feedback for this assignment, since there a lots of things that can go wrong, and the provided testing scripts do not check everything that Potluck checks (like docstrings).

Examples

Optimizing seating arrangements

These examples show what the output from optimizeSeating should look like. Reminder: optimizeSeating is an OPTIONAL extra part.

In []:
optimizeSeating( [ {'name': 'Julia', 'hates': [], 'dislikes': []}, { 'name': 'Yasmin', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, {'name': 'Aaron', 'hates': [], 'dislikes': []}, ], 3 )
Out[]:
[ [ {'name': 'Aaron', 'hates': [], 'dislikes': []}, {'name': 'Julia', 'hates': [], 'dislikes': []}, ], [ { 'name': 'Yasmin', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, ], ]
In []:
optimizeSeating( [ {'name': 'Julia', 'hates': [], 'dislikes': []}, {'name': 'Aaron', 'hates': [], 'dislikes': []}, { 'name': 'Yasmin', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, {'name': 'Sigmund', 'hates': [], 'dislikes': []}, {'name': 'Bellas', 'hates': [], 'dislikes': []}, { 'name': 'Rory', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, {'name': 'Naia', 'hates': [], 'dislikes': []}, { 'name': 'Thomas', 'hates':[ { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, ], 'dislikes': [], }, { 'name': 'Ceres', 'hates': [], 'dislikes': [{'name': 'Naia', 'hates': [], 'dislikes': []}], }, {'name': 'Zeke', 'hates': [], 'dislikes': []}, { 'name': 'Lulu', 'hates':[ { 'name': 'Thomas', 'hates':[ { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, ], 'dislikes': [], }, ], 'dislikes': [], }, ], 5 )
Out[]:
[ [ {'name': 'Bellas', 'hates': [], 'dislikes': []}, { 'name': 'Rory', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, {'name': 'Sigmund', 'hates': [], 'dislikes': []}, {'name': 'Aaron', 'hates': [], 'dislikes': []}, {'name': 'Julia', 'hates': [], 'dislikes': []}, ], [ {'name': 'Zeke', 'hates': [], 'dislikes': []}, {'name': 'Naia', 'hates': [], 'dislikes': []}, { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, { 'name': 'Ceres', 'hates': [], 'dislikes': [{'name': 'Naia', 'hates': [], 'dislikes': []}], }, { 'name': 'Yasmin', 'hates': [], 'dislikes': [{'name': 'Julia', 'hates': [], 'dislikes': []}], }, ], [ { 'name': 'Thomas', 'hates':[ { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, ], 'dislikes': [], }, ], [ { 'name': 'Lulu', 'hates':[ { 'name': 'Thomas', 'hates':[ { 'name': 'Konrad', 'hates': [{'name': 'Aaron', 'hates': [], 'dislikes': []}], 'dislikes': [], }, ], 'dislikes': [], }, ], 'dislikes': [], }, ], ]

Rubric

Group goals:
 
unknown All functions are documented
Each function you define must include a non-empty documentation string as the very first thing in the function.
 
unknown diamondPattern must print the correct output
The output printed when your diamondPattern function is run must match the solution output.
 
unknown optimizeSeating must return the correct result
The result returned when your optimizeSeating function is run must match the solution result.
 
unknown The result of optimizeSeating must have the correct memory structure
The memory structure of the result produced by running optimizeSeating must match that of the result produced by the solution code, including which parts are aliases of each other.
 
unknown Define diamondPattern with 0 parameters
Use def to define diamondPattern with 0 parameters
 
unknown Do not call print
Within the definition of diamondPattern with 0 parameters, do not call print.
 
unknown At least two calls to locally-defined function(s)
Within the definition of diamondPattern with 0 parameters, call at least one custom function at least twice, or at least two different custom functions at least once each. (You are welcome to call more functions or call them more than twice.)
 
unknown Do not use if statements
Within the definition of diamondPattern with 0 parameters, you are not allowed to use `if` statements in this function.
 
unknown Do not use loops
Within the definition of diamondPattern with 0 parameters, you aren't allowed to use loops in this function.
 
unknown Call print
Within the definition of diamondPattern with 0 parameters, call print in at most 10 places.
 
unknown Define diamondPattern with 0 parameters
Use def to define diamondPattern with 0 parameters
 
unknown No more than 8 calls to locally-defined function(s)
Within the definition of diamondPattern with 0 parameters, don't make more than 8 calls to locally-defined functions. Note that you may indirectly make as many calls as you like, only calls within diamondPattern itself are limited.